简单实现TCP客户端重连机制

实现在服务端可能不定时离线的情况下,客户端自动连接服务端

 1 #ifndef _TCP_CLIENT
 2 #define _TCP_CLIENT
 3 
 4 #include <errno.h>
 5 #include <netinet/in.h>
 6 #include <netinet/ip.h>
 7 #include <stdio.h>
 8 #include <stdlib.h>
 9 #include <string.h>
10 #include <strings.h>
11 #include <sys/socket.h>
12 #include <sys/types.h>
13 #include <unistd.h>
14 
15 #include <atomic>
16 #include <thread>
17 
18 #include "log/log.h"
19 
20 class TcpClient {
21    public:
22     TcpClient()
23         : mThread([this]() {
24               while (this->sockInit() == -1) {
25                   LOG_INFO("try to sockInit again");
26                   sleep(1);
27               }
28               mIsSockInit = true;
29           }) {}
30 
31     int sockInit() {
32         // create sock fd
33         if ((mFd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
34             LOG_ERROR("create socket failed");
35             return -1;
36         }
37 
38         // connect to server
39         bzero(&mSin, sizeof(mSin));  //初始值置零
40         mSin.sin_family = AF_INET;
41         mSin.sin_port = htons(mPort);  //转化为NBD
42 
43         if (inet_pton(AF_INET, mIpAddr.c_str(), (void*)&mSin.sin_addr.s_addr) !=
44             1) {
45             LOG_ERROR("inet_pton");
46             return -1;
47         }
48 
49         if (connect(mFd, (struct sockaddr*)&mSin, sizeof(mSin)) < 0) {
50             LOG_ERROR("connect failed");
51             return -1;
52         }
53         LOG_INFO("Client starting ...");
54         return 0;
55     }
56 
57     int sendData(const char* data, size_t len) {
58         if (mIsSockInit) {
59             // judge if the connection is broken
60             int infoLen = sizeof(mInfo);
61             getsockopt(mFd, IPPROTO_TCP, TCP_INFO, &mInfo,
62                        (socklen_t*)&infoLen);
63             if ((mInfo.tcpi_state == TCP_ESTABLISHED)) {
64                 int ret = 0;
65                 if (data != nullptr && len != 0) {
66                     int sendLen = 0;
67                     while (sendLen != len) {
68                         int remainLen = len - sendLen;
69                         ret =
70                             send(mFd, data + sendLen, remainLen, MSG_NOSIGNAL);
71                         sendLen += ret;
72                         LOG_INFO("send len {}", ret);
73                     }
74                 }
75             } else {
76                 LOG_WARN("socket Reconnecting....");
77                 close(mFd);
78                 sleep(1);
79                 sockInit();
80                 LOG_INFO("Socket Reconnnecting success!");
81             }
82         }
83     }
84 
85    private:
86     int mFd{-1};
87 
88     std::thread mThread;
89     std::string mIpAddr{"192.168.0.174"};
90     int mPort{8001};
91     struct sockaddr_in mSin;
92     struct tcp_info mInfo;
93     std::atomic<bool> mIsSockInit{false};
94 };
95 
96 #endif

 在构造函数中,启动一个线程,用于初始化socket连接,如果服务端不在线或有其他故障就一直重复初始化知道connect成功。第一次连接成功后,将mIsSockInit标志置为真。发送数据sendData一般会在另一个数据源线程中循环发送,判断第一次连接成功后就尝试发送数据。在每次发送数据时也要检测连接是否保持,断开的话就重新连接。

send函数中有两点需要注意:1

1. 当IO缓冲区的大小bufSize小于发送的长度len时,send函数实际发送的长度可能会小于len。因此有必要对send的返回值进行校验,将每一次数据包的数据都发送出去。

2. 在linux下编写TCP socket程序时,如果某一端突然退出,导致连接中断,这个时候另一端端如果继续调用send函数发送数据的话,会引发一个信号SIGPIPE,此信号会导致整个进程退出。把flags设置为MSG_NOSIGNAL,则不会导致信号退出。

posted @ 2022-02-09 17:41  朱果果  阅读(2308)  评论(0编辑  收藏  举报