diff --git a/include/Netra.hpp b/include/Netra.hpp index 85aba11..7d43c22 100644 --- a/include/Netra.hpp +++ b/include/Netra.hpp @@ -78,6 +78,83 @@ namespace QCL std::mutex clientsMutex_; ///< 保护clientSockets_的互斥锁 std::vector clientSockets_; ///< 当前所有连接的客户端Socket集合 }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * @class TcpClient + * @brief 简单的TCP客户端类,支持自动连接、消息收发及断线重连 + * + * 该类用于连接指定服务器IP与端口, + * 支持发送与接收字符串消息, + * 并提供自动重连与线程安全的消息接收。 + */ + class TcpClient + { + public: + /** + * @brief 构造函数,指定服务器IP与端口 + * @param serverIP 服务器IP地址 + * @param serverPort 服务器端口号 + */ + TcpClient(const std::string &serverIP, int serverPort); + + /** + * @brief 析构函数,自动断开连接并清理资源 + */ + ~TcpClient(); + + /** + * @brief 连接服务器 + * @return 连接成功返回true,失败返回false + */ + bool connectToServer(); + + /** + * @brief 断开与服务器的连接 + */ + void disconnect(); + + /** + * @brief 向服务器发送字符串消息 + * @param message 要发送的字符串 + */ + void sendToServer(const std::string &message); + + /** + * @brief 从服务器接收数据(单次调用) + * @param flag false: 非阻塞模式, true: 阻塞模式 + * @return 收到的数据字符串(若无数据返回空字符串) + */ + std::string receiveFromServer(bool flag = true); + + /** + * @brief 获取当前连接的服务器IP和端口 + * @return 字符串形式的 "IP:Port" + */ + std::string getServerIPAndPort() const; + + /** + * @brief 判断客户端当前是否已连接 + * @return 已连接返回true,否则返回false + */ + bool isConnected() const; + + private: + /** + * @brief 尝试自动重连服务器(可选) + * @param retryIntervalMs 重连间隔,单位毫秒 + */ + void reconnectLoop(int retryIntervalMs = 3000); + + private: + int clientSock_; ///< 客户端Socket描述符 + std::string serverIP_; ///< 服务器IP地址 + int serverPort_; ///< 服务器端口号 + std::atomic connected_; ///< 连接状态标志(线程安全) + std::atomic running_; ///< 是否保持运行(用于自动重连) + std::thread reconnectThread_; ///< 自动重连线程(可选) + mutable std::mutex socketMutex_; ///< 保护socket的互斥锁 + }; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * @brief 文件写入工具类(线程安全) diff --git a/src/Netra.cpp b/src/Netra.cpp index 3160759..eaa8812 100644 --- a/src/Netra.cpp +++ b/src/Netra.cpp @@ -210,6 +210,123 @@ namespace QCL return result; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + TcpClient::TcpClient(const std::string &serverIP, int serverPort) + : serverIP_(serverIP), serverPort_(serverPort), clientSock_(-1), + connected_(false), running_(false) + { + } + + TcpClient::~TcpClient() + { + disconnect(); + } + + bool TcpClient::connectToServer() + { + std::lock_guard lock(socketMutex_); + + clientSock_ = socket(AF_INET, SOCK_STREAM, 0); + if (clientSock_ < 0) + { + perror("Socket creation failed"); + return false; + } + + sockaddr_in serverAddr{}; + serverAddr.sin_family = AF_INET; + serverAddr.sin_port = htons(serverPort_); + if (inet_pton(AF_INET, serverIP_.c_str(), &serverAddr.sin_addr) <= 0) + { + perror("Invalid address"); + close(clientSock_); + return false; + } + + if (connect(clientSock_, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) + { + perror("Connection failed"); + close(clientSock_); + return false; + } + + connected_ = true; + running_ = true; + std::cout << "Connected to server " << serverIP_ << ":" << serverPort_ << std::endl; + return true; + } + + void TcpClient::disconnect() + { + std::lock_guard lock(socketMutex_); + running_ = false; + if (connected_) + { + close(clientSock_); + connected_ = false; + std::cout << "Disconnected from server" << std::endl; + } + } + + void TcpClient::sendToServer(const std::string &message) + { + std::lock_guard lock(socketMutex_); + if (!connected_) + return; + + ssize_t bytesSent = send(clientSock_, message.c_str(), message.size(), 0); + if (bytesSent <= 0) + { + perror("Send failed"); + connected_ = false; + } + } + + std::string TcpClient::receiveFromServer(bool flag) + { + std::lock_guard lock(socketMutex_); + if (!connected_) + return ""; + + char buffer[1024] = {0}; + int flags = flag ? 0 : MSG_DONTWAIT; + ssize_t bytesRead = recv(clientSock_, buffer, sizeof(buffer) - 1, flags); + if (bytesRead <= 0) + { + if (bytesRead < 0 && errno == EAGAIN) + return ""; // 非阻塞下无数据 + connected_ = false; + perror("Receive failed"); + return ""; + } + return std::string(buffer, bytesRead); + } + + std::string TcpClient::getServerIPAndPort() const + { + return serverIP_ + ":" + std::to_string(serverPort_); + } + + bool TcpClient::isConnected() const + { + return connected_; + } + + void TcpClient::reconnectLoop(int retryIntervalMs) + { + while (running_) + { + if (!connected_) + { + std::cout << "Attempting to reconnect..." << std::endl; + if (connectToServer()) + { + std::cout << "Reconnected successfully!" << std::endl; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(retryIntervalMs)); + } + } + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// WriteFile::WriteFile(const std::string &filePath) : filePath_(filePath) {} @@ -314,7 +431,7 @@ namespace QCL return 0; } - bool WriteFile::writeAfterPatternOrAppend(const std::string &pattern, const std::string &content) + bool WriteFile::writeAfterPatternOrAppend(const std::string &pattern, const std::string &content) { std::lock_guard lock(writeMutex_); @@ -507,16 +624,19 @@ namespace QCL // return lines; std::lock_guard lock(mtx_); - if (!file_.is_open()) { + if (!file_.is_open()) + { file_.open(filename_, std::ios::in | std::ios::binary); - if (!file_.is_open()) return {}; + if (!file_.is_open()) + return {}; } file_.clear(); file_.seekg(0, std::ios::beg); - + std::vector lines; std::string line; - while (std::getline(file_, line)) lines.push_back(line); + while (std::getline(file_, line)) + lines.push_back(line); return lines; }