簡單實現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,則不會導致信號退出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM