diff --git a/utils/TcpModule/readme.md b/utils/TcpModule/readme.md index ad41416b..ff2f0267 100644 --- a/utils/TcpModule/readme.md +++ b/utils/TcpModule/readme.md @@ -31,3 +31,29 @@ * **TcpClientAcceptImpl.h/TcpClientAcceptImpl.cpp**:实现了ITcpClientAccept接口,处理客户端连接的接受和数据交换。 +# 一些易混淆的点 +1. 客户端创建的套接字包含指定`目标的IP地址和端口号`(TcpClientImpl类中的init函数),服务器创建的套接字只包含`本机IP地址和监听端口`。 + +2. 在网络编程中,服务器通常会在一个监听端口上等待来自客户端的连接请求。当服务器接受到一个连接请求时,它会创建一个新的套接字(或文件描述符)来表示这个新建立的连接,并且通常会为这个新的连接设置一系列的事件回调函数。 + +3. 客户端实例(TcpClientImpl类对象)进行init操作时,会创建一个套接字并对目标服务器发出连接请求并启动事件循环(Loop函数),然后客户端实例会一直进行事件循环(监听是否有读写操作)直至连接关闭(Close函数)。 + +# 该模块的实现过程 +## 服务器端: +1. 创建服务器端套接字:使用 hloop_create_tcp_server 函数创建一个新的 TCP 服务器端套接字,并指定要监听的 IP 地址和端口号。 +2. 设置回调函数:为服务器端套接字设置回调函数,包括: +on_accept:当有新的客户端尝试连接时调用。 +on_close:当服务器端套接字关闭时调用。 +3. 启动事件循环:使用 hloop_run 启动服务器的事件循环,等待客户端的连接请求。 +4. 接受连接:在 on_accept 回调函数中,接受客户端的连接请求,并创建用于该连接的新套接字。 +5. 创建客户端接受对象:为新的客户端连接创建一个 TcpClientAcceptImpl 对象,并设置相应的回调函数。 +管理客户端连接:将新创建的客户端接受对象添加到管理容器中,以便跟踪和管理。 +## 客户端: +1. 创建客户端套接字:使用 hio_create_socket 函数创建一个新的 TCP 客户端套接字。 +2. 设置回调函数:为客户端套接字设置回调函数,包括: +on_connect:当连接成功建立时调用。 +on_close:当连接关闭时调用。 +on_message:当接收到服务器发送的数据时调用。 +3. 连接到服务器:使用 hio_connect 函数向服务器发起连接请求。 +4. 启动事件循环:使用 hloop_run 启动客户端的事件循环,等待连接结果和数据传输。 +5. 处理连接结果:在 on_connect 回调函数中处理连接结果,如果连接成功,可以开始发送和接收数据。 \ No newline at end of file diff --git a/utils/TcpModule/src/TcpClientImpl.cpp b/utils/TcpModule/src/TcpClientImpl.cpp index 8b420736..58c723e2 100644 --- a/utils/TcpModule/src/TcpClientImpl.cpp +++ b/utils/TcpModule/src/TcpClientImpl.cpp @@ -26,7 +26,7 @@ /** * @brief Called when data is received on a TCP connection. * - * @param io Socket handle + * @param io Client's socket * @param buf The transmitted data content * @param len Byte length of transmitted data */ @@ -38,17 +38,18 @@ static void on_message(hio_t *io, void *buf, int len) } /** * @brief Called when TCP connection is established. - * @param io Socket handle + * @param io Client's socket */ static void on_connect(hio_t *io) { LogInfo("onconnect: connfd=%d\n", hio_fd(io)); hio_setcb_read(io, on_message); + /// Start a read operation. Read data if it arrives. hio_read(io); } /** * @brief Called when tcp connection is disconnected. - * @param io Socket handle + * @param io Client't socket */ static void on_close(hio_t *io) { @@ -60,8 +61,8 @@ TcpClientImpl::TcpClientImpl(const TcpClientParam ¶m, const void *object) : { } /** - * @brief Initialize TCP clients, create event loops, I/O objects, and set callback functions for connection and - * closure. + * @brief Create a socket and connect it, and start an event loop to listen to io object operations (read and write, + * etc.) */ void TcpClientImpl::Init(void) { @@ -72,6 +73,19 @@ void TcpClientImpl::Init(void) LogError("TcpClientImpl::Init hloop_new failed.\n"); return; } + /** + * @brief The client's socket, which is used to actively send connection requests to the server and handle related + * I/O operations, has no listening function. + * @param mLoop Event loop. + * When I/O events (connection closed, connection successful, data readable, etc.) occur, the event loop will call + * the corresponding callback function to handle them. + * + * @param mParam.mIp Server IP address + * @param mParam.mPort Server port + * @param HIO_TYPE_TCP TCP connection + * @param HIO_CLIENT_SIDE Indicates that the socket will be used as a client. + * + */ hio_t *io = hio_create_socket(mLoop, mParam.mIp, mParam.mPort, HIO_TYPE_TCP, HIO_CLIENT_SIDE); if (nullptr == io) { LogError("TcpClientImpl::Init hio_create_socket failed.\n"); @@ -80,9 +94,9 @@ void TcpClientImpl::Init(void) hevent_set_userdata(io, this); /// Set the callback function of successful connection, and there is no connection operation. hio_setcb_connect(io, on_connect); + /// Set the callback function to close the connection, and there is no connection operation. hio_setcb_close(io, on_close); - /// Connection operation - hio_connect(io); + hio_connect(io); ///< Connection operation mIo = io; std::shared_ptr impl = std::dynamic_pointer_cast(shared_from_this()); auto recvThread = [](std::shared_ptr tcpClient) { @@ -91,7 +105,7 @@ void TcpClientImpl::Init(void) mTcpClientThread = std::thread(recvThread, impl); } /** - * @brief De-initialize the TCP client, close the I/O object and wait for the receiving thread to end. + * @brief De-initialize the TCP client, close the client't socket and wait for the event cycle to end. */ void TcpClientImpl::UnInit(void) { @@ -104,6 +118,12 @@ void TcpClientImpl::UnInit(void) mTcpClientThread.join(); } } +/** + * @brief Read the data sent by the server in tcp connection. + * + * @param data Data content sent by the server + * @param length Byte length of data content sent by the server. + */ void TcpClientImpl::Readed(const void *data, size_t length) { if (nullptr != mParam.mReadFunc) { @@ -112,6 +132,13 @@ void TcpClientImpl::Readed(const void *data, size_t length) } LogError("mParam.mReadFunc is nullptr.\n"); } +/** + * @brief The client writes data to the server, and the writing operation can only be performed in the connected state. + * + * @param buf Content of written data + * @param bufLenght Byte length of written data + * @return ssize_t + */ ssize_t TcpClientImpl::Write(const void *buf, const size_t bufLenght) { std::lock_guard locker(mMutex); @@ -122,6 +149,10 @@ ssize_t TcpClientImpl::Write(const void *buf, const size_t bufLenght) /// Returns the byte length of a packet.If it fails, an error code is returned. return hio_write(mIo, buf, bufLenght); } +/** + * @brief Close the tcp connection and stop the event loop (reading and writing I/O objects). + * + */ void TcpClientImpl::Closed(void) { std::lock_guard locker(mMutex); @@ -157,15 +188,12 @@ std::shared_ptr *NewTcpClient(const TcpClientParam ¶m) return nullptr; } TcpClient tmp; + /// Initialize impl with tmp memcpy((void *)impl, (void *)&tmp, sizeof(TcpClient)); impl->mHeader.mCheckName = GetTcpClientModuleName(); - /** - * @brief ObjectThis points to the first address of the impl and offsets the address by ITcpClientHeader bytes, - * that is, skips the mHeader part of the impl. - */ + /// ObjectThis is actually a pointer to mTcpClient in the TcpClient structure. std::shared_ptr *objectThis = (std::shared_ptr *)(((char *)impl) + sizeof(ITcpClientHeader)); impl->mTcpClient = std::make_shared(param, objectThis); - /// ObjectThis is used to verify whether the client is legal. return objectThis; } \ No newline at end of file diff --git a/utils/TcpModule/src/TcpClientImpl.h b/utils/TcpModule/src/TcpClientImpl.h index 49f60eb2..b2023168 100644 --- a/utils/TcpModule/src/TcpClientImpl.h +++ b/utils/TcpModule/src/TcpClientImpl.h @@ -27,20 +27,21 @@ class TcpClientImpl : public ITcpClient, public std::enable_shared_from_this *NewTcpClient(const TcpClientParam ¶m); #endif \ No newline at end of file diff --git a/utils/TcpModule/src/TcpServerImpl.cpp b/utils/TcpModule/src/TcpServerImpl.cpp index 61147c62..16a8e5bd 100644 --- a/utils/TcpModule/src/TcpServerImpl.cpp +++ b/utils/TcpModule/src/TcpServerImpl.cpp @@ -25,9 +25,9 @@ #include #include /** - * @brief Disconnect the connected client. + * @brief Handle the close connection event. * - * @param io Socket + * @param io Server's socket */ static void on_close(hio_t *io) { @@ -150,15 +150,13 @@ TcpServerImpl::TcpServerImpl(const TcpServerParam param) : mParam(param) void TcpServerImpl::Init(void) { constexpr int NO_FALGS = 0; - /// Initialize event loop /// mLoop is used to manage all I/O objects and the events that occur on them. mLoop = hloop_new(NO_FALGS); if (nullptr == mLoop) { LogError("hloop_new failed\n"); return; } - /// Create a listening event to listen whether a new client sends a connection request. - /// Listenio is part of mLoop. + /// Create a socket to listen whether a new client sends a connection request. hio_t *listenio = hloop_create_tcp_server(mLoop, mParam.mIp, mParam.mPort, on_accept); if (nullptr == listenio) { LogError("hloop_create_tcp_server failed\n"); @@ -256,9 +254,9 @@ std::shared_ptr *TcpServerImpl::GetClient(hio_t *io) return nullptr; } /** - * @brief Remove the data element in the map, that is, the connected client. + * @brief Removes a specific connection from the list of client connections maintained by the server. * - * @param io Socket + * @param io A server-side socket associated with an established client connection. */ void TcpServerImpl::RemoveClient(hio_t *io) { @@ -316,6 +314,7 @@ std::shared_ptr *NewTcpServer(const TcpServerParam ¶m) return nullptr; } TcpServer tmp; + /// Initialize impl with tmp memcpy((void *)impl, (void *)&tmp, sizeof(TcpServer)); impl->mHeader.mCheckName = GetTcpServerModuleName(); impl->mTcpServer = std::make_shared(param); diff --git a/utils/TcpModule/src/TcpServerImpl.h b/utils/TcpModule/src/TcpServerImpl.h index e4c19de5..74a7a93b 100644 --- a/utils/TcpModule/src/TcpServerImpl.h +++ b/utils/TcpModule/src/TcpServerImpl.h @@ -54,7 +54,7 @@ public: virtual ~TcpServerImpl() = default; void Init(void) override; void UnInit(void) override; - void Loop(void); /// Run an event loop and release resources after completion or error. + void Loop(void); /// Run an event loop and release resources after completion or error. void AddClient(hio_t *io); std::shared_ptr *GetClient(hio_t *io); void RemoveClient(hio_t *io); /// Remove the data element in the map, that is, the connected client. @@ -64,7 +64,7 @@ public: private: std::mutex mMutex; /// A mutex lock used to synchronize access to shared resources. hloop_t *mLoop; /// Event loop, listening for all io objects - hio_t *mIo; /// listen whether a new client sends a connection request. + hio_t *mIo; /// A server socket to listen whether a new client sends a connection request. const TcpServerParam mParam; std::thread mTcpServerThread; std::map *> mClients;