更新 main.cpp 及相关代码
This commit is contained in:
10
README.md
10
README.md
@@ -1,5 +1,5 @@
|
|||||||
# NetraLib
|
# NetraLib
|
||||||
c/c++基本开发库
|
c/c++基本开发库
|
||||||
|
|
||||||
# TCP 服务端操作
|
# TCP 服务端操作
|
||||||
包括多线程客户端连接,指定客户端数据的收发等等功能
|
包括多线程客户端连接,指定客户端数据的收发等等功能
|
||||||
|
@@ -1,75 +1,80 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "QCL_Include.hpp"
|
#include "QCL_Include.hpp"
|
||||||
|
|
||||||
namespace QCL
|
|
||||||
{
|
namespace QCL
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
{
|
||||||
/**
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
* @class TcpServer
|
/**
|
||||||
* @brief 简单的多线程TCP服务器类,支持多个客户端连接,数据收发及断开处理
|
* @class TcpServer
|
||||||
*
|
* @brief 简单的多线程TCP服务器类,支持多个客户端连接,数据收发及断开处理
|
||||||
* 该类使用一个线程专门用于监听客户端连接,
|
*
|
||||||
* 每当有客户端连接成功时,为其创建一个独立线程处理该客户端的数据收发。
|
* 该类使用一个线程专门用于监听客户端连接,
|
||||||
* 线程安全地管理所有客户端Socket句柄。
|
* 每当有客户端连接成功时,为其创建一个独立线程处理该客户端的数据收发。
|
||||||
*/
|
* 线程安全地管理所有客户端Socket句柄。
|
||||||
class TcpServer
|
*/
|
||||||
{
|
class TcpServer
|
||||||
public:
|
{
|
||||||
/**
|
public:
|
||||||
* @brief 构造函数,指定监听端口
|
/**
|
||||||
* @param port 服务器监听端口号
|
* @brief 构造函数,指定监听端口
|
||||||
*/
|
* @param port 服务器监听端口号
|
||||||
TcpServer(int port);
|
*/
|
||||||
|
TcpServer(int port);
|
||||||
/**
|
|
||||||
* @brief 析构函数,自动调用 stop() 停止服务器并清理资源
|
/**
|
||||||
*/
|
* @brief 析构函数,自动调用 stop() 停止服务器并清理资源
|
||||||
~TcpServer();
|
*/
|
||||||
|
~TcpServer();
|
||||||
/**
|
|
||||||
* @brief 启动服务器,创建监听socket,开启监听线程
|
/**
|
||||||
* @return 启动成功返回true,失败返回false
|
* @brief 启动服务器,创建监听socket,开启监听线程
|
||||||
*/
|
* @return 启动成功返回true,失败返回false
|
||||||
bool start();
|
*/
|
||||||
|
bool start();
|
||||||
/**
|
|
||||||
* @brief 停止服务器,关闭所有连接,释放资源,等待所有线程退出
|
/**
|
||||||
*/
|
* @brief 停止服务器,关闭所有连接,释放资源,等待所有线程退出
|
||||||
void stop();
|
*/
|
||||||
|
void stop();
|
||||||
/**
|
|
||||||
* @brief 发送消息给指定客户端
|
/**
|
||||||
* @param clientSock 客户端Socket描述符
|
* @brief 发送消息给指定客户端
|
||||||
* @param message 发送的字符串消息
|
* @param clientSock 客户端Socket描述符
|
||||||
*/
|
* @param message 发送的字符串消息
|
||||||
void sendToClient(int clientSock, const std::string &message);
|
*/
|
||||||
|
void sendToClient(int clientSock, const std::string &message);
|
||||||
/**
|
|
||||||
* @brief 从指定客户端接收数据(单次调用)
|
/**
|
||||||
* @param clientSock 客户端Socket描述符
|
* @brief 从指定客户端接收数据(单次调用)
|
||||||
*/
|
* @param clientSock 客户端Socket描述符
|
||||||
char *receiveFromClient(int clientSock);
|
*/
|
||||||
|
char *receiveFromClient(int clientSock);
|
||||||
/**
|
|
||||||
* @brief 获取当前所有已连接客户端Socket的副本
|
/**
|
||||||
* @return 包含所有客户端Socket的vector,线程安全
|
* @brief 获取当前所有已连接客户端Socket的副本
|
||||||
*/
|
* @return 包含所有客户端Socket的vector,线程安全
|
||||||
std::vector<int> getClientSockets();
|
*/
|
||||||
|
std::vector<int> getClientSockets();
|
||||||
private:
|
|
||||||
/**
|
private:
|
||||||
* @brief 监听并接受新的客户端连接(运行在独立线程中)
|
/**
|
||||||
*/
|
* @brief 监听并接受新的客户端连接(运行在独立线程中)
|
||||||
void acceptClients();
|
*/
|
||||||
|
void acceptClients();
|
||||||
private:
|
|
||||||
int serverSock_; ///< 服务器监听Socket描述符
|
private:
|
||||||
int port_; ///< 服务器监听端口
|
int serverSock_; ///< 服务器监听Socket描述符
|
||||||
std::atomic<bool> running_; ///< 服务器运行状态标志(线程安全)
|
int port_; ///< 服务器监听端口
|
||||||
std::vector<std::thread> clientThreads_; ///< 用于处理每个客户端的线程集合
|
std::atomic<bool> running_; ///< 服务器运行状态标志(线程安全)
|
||||||
std::thread acceptThread_; ///< 负责监听新连接的线程
|
std::vector<std::thread> clientThreads_; ///< 用于处理每个客户端的线程集合
|
||||||
std::mutex clientsMutex_; ///< 保护clientSockets_的互斥锁
|
std::thread acceptThread_; ///< 负责监听新连接的线程
|
||||||
std::vector<int> clientSockets_; ///< 当前所有连接的客户端Socket集合
|
std::mutex clientsMutex_; ///< 保护clientSockets_的互斥锁
|
||||||
};
|
std::vector<int> clientSockets_; ///< 当前所有连接的客户端Socket集合
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
};
|
||||||
}
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// 屏蔽所有信号
|
||||||
|
void blockAllSignals();
|
||||||
|
|
||||||
|
}
|
||||||
|
@@ -1,47 +1,48 @@
|
|||||||
// 包含常用 STL 头文件 + c/c++开发中的核心组件
|
// 包含常用 STL 头文件 + c/c++开发中的核心组件
|
||||||
|
|
||||||
#ifndef QCL_INCLUDE_HPP
|
#ifndef QCL_INCLUDE_HPP
|
||||||
#define QCL_INCLUDE_HPP
|
#define QCL_INCLUDE_HPP
|
||||||
|
|
||||||
// C++ 标准库 - 基础功能
|
// C++ 标准库 - 基础功能
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <csignal>
|
||||||
// 容器与算法
|
|
||||||
#include <vector>
|
// 容器与算法
|
||||||
#include <list>
|
#include <vector>
|
||||||
#include <deque>
|
#include <list>
|
||||||
#include <map>
|
#include <deque>
|
||||||
#include <set>
|
#include <map>
|
||||||
#include <unordered_map>
|
#include <set>
|
||||||
#include <unordered_set>
|
#include <unordered_map>
|
||||||
#include <stack>
|
#include <unordered_set>
|
||||||
#include <queue>
|
#include <stack>
|
||||||
#include <algorithm>
|
#include <queue>
|
||||||
#include <numeric>
|
#include <algorithm>
|
||||||
#include <iterator>
|
#include <numeric>
|
||||||
|
#include <iterator>
|
||||||
// 字符串与流处理
|
|
||||||
#include <sstream>
|
// 字符串与流处理
|
||||||
#include <fstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
// 多线程支持(C++11 起)
|
|
||||||
#include <thread>
|
// 多线程支持(C++11 起)
|
||||||
#include <mutex>
|
#include <thread>
|
||||||
#include <atomic>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <atomic>
|
||||||
|
#include <condition_variable>
|
||||||
// 网络支持(平台相关)
|
|
||||||
#include <sys/socket.h>
|
// 网络支持(平台相关)
|
||||||
#include <netinet/in.h>
|
#include <sys/socket.h>
|
||||||
#include <arpa/inet.h>
|
#include <netinet/in.h>
|
||||||
#include <unistd.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <unistd.h>
|
||||||
#endif // QCL_INCLUDE_HPP
|
|
||||||
|
#endif // QCL_INCLUDE_HPP
|
||||||
|
376
src/Netra.cpp
376
src/Netra.cpp
@@ -1,185 +1,193 @@
|
|||||||
#include "Netra.hpp"
|
#include "Netra.hpp"
|
||||||
|
|
||||||
namespace QCL
|
namespace QCL
|
||||||
{
|
{
|
||||||
TcpServer::TcpServer(int port)
|
TcpServer::TcpServer(int port)
|
||||||
: port_(port), running_(false), serverSock_(-1) {}
|
: port_(port), running_(false), serverSock_(-1) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 析构函数中调用stop()确保服务器资源被释放
|
* @brief 析构函数中调用stop()确保服务器资源被释放
|
||||||
*/
|
*/
|
||||||
TcpServer::~TcpServer()
|
TcpServer::~TcpServer()
|
||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 启动服务器:
|
* @brief 启动服务器:
|
||||||
* 1. 创建监听socket(TCP)
|
* 1. 创建监听socket(TCP)
|
||||||
* 2. 绑定端口
|
* 2. 绑定端口
|
||||||
* 3. 监听端口
|
* 3. 监听端口
|
||||||
* 4. 启动监听线程acceptThread_
|
* 4. 启动监听线程acceptThread_
|
||||||
*
|
*
|
||||||
* @return 成功返回true,失败返回false
|
* @return 成功返回true,失败返回false
|
||||||
*/
|
*/
|
||||||
bool TcpServer::start()
|
bool TcpServer::start()
|
||||||
{
|
{
|
||||||
// 创建socket
|
// 创建socket
|
||||||
serverSock_ = socket(AF_INET, SOCK_STREAM, 0);
|
serverSock_ = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (serverSock_ < 0)
|
if (serverSock_ < 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Socket 创建失败\n";
|
std::cerr << "Socket 创建失败\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置socket地址结构
|
// 设置socket地址结构
|
||||||
sockaddr_in serverAddr;
|
sockaddr_in serverAddr;
|
||||||
std::memset(&serverAddr, 0, sizeof(serverAddr));
|
std::memset(&serverAddr, 0, sizeof(serverAddr));
|
||||||
serverAddr.sin_family = AF_INET;
|
serverAddr.sin_family = AF_INET;
|
||||||
serverAddr.sin_port = htons(port_); // 端口转网络字节序
|
serverAddr.sin_port = htons(port_); // 端口转网络字节序
|
||||||
serverAddr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡IP
|
serverAddr.sin_addr.s_addr = INADDR_ANY; // 监听所有网卡IP
|
||||||
|
|
||||||
// 允许端口重用,防止服务器异常关闭后端口被占用
|
// 允许端口重用,防止服务器异常关闭后端口被占用
|
||||||
int opt = 1;
|
int opt = 1;
|
||||||
setsockopt(serverSock_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
setsockopt(serverSock_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||||||
|
|
||||||
// 绑定端口
|
// 绑定端口
|
||||||
if (bind(serverSock_, (sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
|
if (bind(serverSock_, (sockaddr *)&serverAddr, sizeof(serverAddr)) < 0)
|
||||||
{
|
{
|
||||||
std::cerr << "绑定失败\n";
|
std::cerr << "绑定失败\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开始监听,最大等待连接数为5
|
// 开始监听,最大等待连接数为5
|
||||||
if (listen(serverSock_, 5) < 0)
|
if (listen(serverSock_, 5) < 0)
|
||||||
{
|
{
|
||||||
std::cerr << "监听失败\n";
|
std::cerr << "监听失败\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置运行标志为true
|
// 设置运行标志为true
|
||||||
running_ = true;
|
running_ = true;
|
||||||
|
|
||||||
// 启动专门接受客户端连接的线程
|
// 启动专门接受客户端连接的线程
|
||||||
acceptThread_ = std::thread(&TcpServer::acceptClients, this);
|
acceptThread_ = std::thread(&TcpServer::acceptClients, this);
|
||||||
|
|
||||||
std::cout << "服务器启动,监听端口:" << port_ << std::endl;
|
std::cout << "服务器启动,监听端口:" << port_ << std::endl;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 停止服务器:
|
* @brief 停止服务器:
|
||||||
* 1. 设置运行标志为false,通知线程退出
|
* 1. 设置运行标志为false,通知线程退出
|
||||||
* 2. 关闭监听socket
|
* 2. 关闭监听socket
|
||||||
* 3. 关闭所有客户端socket,清理客户端列表
|
* 3. 关闭所有客户端socket,清理客户端列表
|
||||||
* 4. 等待所有线程退出
|
* 4. 等待所有线程退出
|
||||||
*/
|
*/
|
||||||
void TcpServer::stop()
|
void TcpServer::stop()
|
||||||
{
|
{
|
||||||
running_ = false;
|
running_ = false;
|
||||||
|
|
||||||
if (serverSock_ >= 0)
|
if (serverSock_ >= 0)
|
||||||
{
|
{
|
||||||
close(serverSock_);
|
close(serverSock_);
|
||||||
serverSock_ = -1;
|
serverSock_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// 线程安全关闭所有客户端socket
|
// 线程安全关闭所有客户端socket
|
||||||
std::lock_guard<std::mutex> lock(clientsMutex_);
|
std::lock_guard<std::mutex> lock(clientsMutex_);
|
||||||
for (int sock : clientSockets_)
|
for (int sock : clientSockets_)
|
||||||
{
|
{
|
||||||
close(sock);
|
close(sock);
|
||||||
}
|
}
|
||||||
clientSockets_.clear();
|
clientSockets_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 等待监听线程退出
|
// 等待监听线程退出
|
||||||
if (acceptThread_.joinable())
|
if (acceptThread_.joinable())
|
||||||
acceptThread_.join();
|
acceptThread_.join();
|
||||||
|
|
||||||
// 等待所有客户端处理线程退出
|
// 等待所有客户端处理线程退出
|
||||||
for (auto &t : clientThreads_)
|
for (auto &t : clientThreads_)
|
||||||
{
|
{
|
||||||
if (t.joinable())
|
if (t.joinable())
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "服务器已停止\n";
|
std::cout << "服务器已停止\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief acceptClients函数循环监听客户端连接请求
|
* @brief acceptClients函数循环监听客户端连接请求
|
||||||
* 每当accept成功:
|
* 每当accept成功:
|
||||||
* 1. 打印客户端IP和Socket信息
|
* 1. 打印客户端IP和Socket信息
|
||||||
* 2. 线程安全地将客户端Socket加入clientSockets_列表
|
* 2. 线程安全地将客户端Socket加入clientSockets_列表
|
||||||
* 3. 创建新线程调用handleClient处理该客户端收发
|
* 3. 创建新线程调用handleClient处理该客户端收发
|
||||||
*/
|
*/
|
||||||
void TcpServer::acceptClients()
|
void TcpServer::acceptClients()
|
||||||
{
|
{
|
||||||
while (running_)
|
while (running_)
|
||||||
{
|
{
|
||||||
sockaddr_in clientAddr;
|
sockaddr_in clientAddr;
|
||||||
socklen_t clientLen = sizeof(clientAddr);
|
socklen_t clientLen = sizeof(clientAddr);
|
||||||
int clientSock = accept(serverSock_, (sockaddr *)&clientAddr, &clientLen);
|
int clientSock = accept(serverSock_, (sockaddr *)&clientAddr, &clientLen);
|
||||||
if (clientSock < 0)
|
if (clientSock < 0)
|
||||||
{
|
{
|
||||||
if (running_)
|
if (running_)
|
||||||
std::cerr << "接受连接失败\n";
|
std::cerr << "接受连接失败\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将客户端IP转换成字符串格式打印
|
// 将客户端IP转换成字符串格式打印
|
||||||
char clientIP[INET_ADDRSTRLEN];
|
char clientIP[INET_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET, &(clientAddr.sin_addr), clientIP, INET_ADDRSTRLEN);
|
inet_ntop(AF_INET, &(clientAddr.sin_addr), clientIP, INET_ADDRSTRLEN);
|
||||||
std::cout << "客户端连接,IP: " << clientIP << ", Socket: " << clientSock << std::endl;
|
std::cout << "客户端连接,IP: " << clientIP << ", Socket: " << clientSock << std::endl;
|
||||||
|
|
||||||
{
|
{
|
||||||
// 加锁保护共享的clientSockets_容器
|
// 加锁保护共享的clientSockets_容器
|
||||||
std::lock_guard<std::mutex> lock(clientsMutex_);
|
std::lock_guard<std::mutex> lock(clientsMutex_);
|
||||||
clientSockets_.push_back(clientSock);
|
clientSockets_.push_back(clientSock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 发送消息给指定客户端
|
* @brief 发送消息给指定客户端
|
||||||
* @param clientSock 客户端socket
|
* @param clientSock 客户端socket
|
||||||
* @param message 发送消息内容
|
* @param message 发送消息内容
|
||||||
*/
|
*/
|
||||||
void TcpServer::sendToClient(int clientSock, const std::string &message)
|
void TcpServer::sendToClient(int clientSock, const std::string &message)
|
||||||
{
|
{
|
||||||
send(clientSock, message.c_str(), message.size(), 0);
|
send(clientSock, message.c_str(), message.size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 单次接收指定客户端数据
|
* @brief 单次接收指定客户端数据
|
||||||
* @param clientSock 客户端socket
|
* @param clientSock 客户端socket
|
||||||
*
|
*
|
||||||
* 注意:此函数在当前设计中未在线程中使用,仅演示用。
|
* 注意:此函数在当前设计中未在线程中使用,仅演示用。
|
||||||
*/
|
*/
|
||||||
char *TcpServer::receiveFromClient(int clientSock)
|
char *TcpServer::receiveFromClient(int clientSock)
|
||||||
{
|
{
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
std::memset(buffer, 0, sizeof(buffer));
|
std::memset(buffer, 0, sizeof(buffer));
|
||||||
|
|
||||||
ssize_t bytesReceived = recv(clientSock, buffer, sizeof(buffer) - 1, 0);
|
ssize_t bytesReceived = recv(clientSock, buffer, sizeof(buffer) - 1, 0);
|
||||||
if (bytesReceived <= 0)
|
if (bytesReceived <= 0)
|
||||||
{
|
{
|
||||||
std::cerr << "接收数据失败或客户端断开连接\n";
|
std::cerr << "接收数据失败或客户端断开连接\n";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return strdup(buffer); // 返回动态分配的字符串副本
|
return strdup(buffer); // 返回动态分配的字符串副本
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取当前所有客户端Socket副本(线程安全)
|
* @brief 获取当前所有客户端Socket副本(线程安全)
|
||||||
* @return 包含所有客户端socket的vector副本
|
* @return 包含所有客户端socket的vector副本
|
||||||
*/
|
*/
|
||||||
std::vector<int> TcpServer::getClientSockets()
|
std::vector<int> TcpServer::getClientSockets()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(clientsMutex_);
|
std::lock_guard<std::mutex> lock(clientsMutex_);
|
||||||
return clientSockets_;
|
return clientSockets_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 屏蔽所有信号
|
||||||
|
void blockAllSignals()
|
||||||
|
{
|
||||||
|
// 忽略全部的信号
|
||||||
|
for (int ii = 1; ii <= 64; ii++)
|
||||||
|
signal(ii, SIG_IGN);
|
||||||
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user