實現在服務端可能不定時離線的情況下,客戶端自動連接服務端
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,則不會導致信號退出。