更新 main.cpp 及相关代码

This commit is contained in:
2025-07-31 14:47:51 +08:00
parent 525060a52e
commit 746eb8bb55
4 changed files with 325 additions and 311 deletions

View File

@@ -1,5 +1,5 @@
# NetraLib # NetraLib
c/c++基本开发库 c/c++基本开发库
# TCP 服务端操作 # TCP 服务端操作
包括多线程客户端连接,指定客户端数据的收发等等功能 包括多线程客户端连接,指定客户端数据的收发等等功能

View File

@@ -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();
}

View File

@@ -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

View File

@@ -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. 创建监听socketTCP * 1. 创建监听socketTCP
* 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);
}
} }