• 简单实现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,则不会导致信号退出。

  • 相关阅读:
    Spring源码加载BeanDefinition过程
    设计模式之☞委派模式,通俗易懂,一学就会!!!
    SpringMvc 跨域处理
    寻找全排列的下一个数
    使用两个栈实现队列
    无序数组在排序后的最大相邻查
    判断一个数是否是2的幂
    最大公约数
    最小栈的实现
    js 原型与原型链
  • 原文地址:https://www.cnblogs.com/y4247464/p/15876036.html
Copyright © 2020-2023  润新知