实现在服务端可能不定时离线的情况下,客户端自动连接服务端
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,则不会导致信号退出。