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