实现TCP Connect的断线重连机制:策略与实践


🍑个人主页:Jupiter. 🚀 所属专栏:Linux从入门到进阶 欢迎大家点赞收藏评论😊


断线重连机制,它成为确保应用在网络不稳定情况下仍能持续提供服务的关键技术之一。本文旨在深入探讨TCP(传输控制协议)连接中的断线重连机制,解析其工作原理、设计原则、实现策略以及在实际应用中的挑战与解决方案。试着写一个客户端重连的代码,模拟并理解一些客户端行为,比如游戏客户端等。

断线重连代码

cpp 复制代码
#include <iostream>
#include <string>
#include <memory>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

enum Status
{
    NEW,          // 新建状态,就是单纯的连接
    CONNECTING,   // 正在连接,仅仅方便查询 conn 状态
    CONNECTED,    // 连接或者重连成功
    DISCONNECTED, // 重连失败
    CLOSED        // 连接失败,经历重连,无法连接
};

const static int defaultretryinterval = 1;  // 重试时间间隔
const static int defaultmaxretries = 5;     // 最大重试次数
const static int defaultsockfd = -1;        // 默认的sockfd

class ConnectClient
{
public:
    ConnectClient(std::string &serverip, uint16_t serverport)   
        : _serverip(serverip),
          _serverport(serverport),
          _sockfd(defaultsockfd),
          _retry_interval(defaultretryinterval),
          _max_retries(defaultmaxretries),
          _status(Status::NEW)
    {
    }
    void Connect() // 建立连接
    {
        // 创建 流式socket
        _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            std::cout << "socket create fail..." << std::endl;
            exit(2);
        }

        // 连接
        struct sockaddr_in serveraddr;
        memset(&serveraddr, 0, sizeof(serveraddr));
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port = _serverport;
        serveraddr.sin_addr.s_addr = inet_addr(_serverip.c_str());

        int n = connect(_sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
        if (n < 0) // 连接失败
        {
            Disconnect(); // 重置_sockfd
            _status = Status::DISCONNECTED;
            return;
        }
        // 连接成功
        _status = Status::CONNECTED;
        std::cout << "connect success..." << std::endl;
    }
    void Reconnect() // 重新连接
    {
        _status = Status::CONNECTING;
        int cnt = 1;
        while (true)
        {
            Connect();
            if (_status == Status::CONNECTED)
            {
                return;
            }
            std::cout << "重连次数: " << cnt << ", 最大上限: " << _max_retries << std::endl;
            cnt++;
            if (cnt > _max_retries)   //超过连接设置的最大次数,直接连接失败,关闭client
            {
                _status = Status::CLOSED;
                return;
            }
            sleep(defaultretryinterval);
        }
    }

    void process()  //客服端连接成功执行的业务
    {
        while (true)
        {
            std::string str = "Hello world!";
            ssize_t n = ::send(_sockfd, str.c_str(), str.size(), 0);
            if (n > 0) // 发送成功
            {
                char buffer[1024]; // 接收buffer
                ssize_t m = ::recv(_sockfd, buffer, sizeof(buffer) - 1, 0);
                if (m > 0) // 接收成功
                {
                    buffer[m] = 0;
                    std::cout << "server echo#" << buffer << std::endl;
                }
                else // 接收失败
                {
                    _status = Status::DISCONNECTED; // 接受失败,说明连接断开了 ,设置状态,重新连接
                    break;
                }
            }
            else // 发送失败
            {
                _status = Status::CLOSED; // 连接成功,但是发送失败,说明服务器异常,直接将Client关闭
                break;
            }
        }
    }
    void Disconnect()   //关闭Client
    {
        if (_sockfd > defaultsockfd)
        {
            ::close(_sockfd);
            _status = Status::CLOSED;
            _sockfd = defaultsockfd;
        }
    }

    Status GetStatus() // 获取当前状态
    {
        return _status;
    }

    ~ConnectClient()
    {
    }

private:
    int _sockfd;
    uint16_t _serverport;     // server port 端口号
    std::string _serverip;    // server ip 地址
    int _retry_interval;      // 重试时间间隔
    int _max_retries;         // 重试次数
    Status _status;           // 连接状态
};

class TcpClinet
{
public:
    TcpClinet(std::string &serverip, uint16_t serverport)
        : _connect(serverip, serverport)
    {
    }
    void Excute()     // 执行client
    {
        while (true)
        {
            switch (_connect.GetStatus())
            {
            case Status::NEW:          //如果状态是新创建,则连接
                _connect.Connect();
                break;
            case Status::CONNECTED:    //已经建立连接了
                _connect.process();    //进行通信
                break;
            case Status::DISCONNECTED:  //断开连接了
                _connect.Reconnect();   //进行重连
                break;
            case Status::CLOSED:        //关闭
                _connect.Disconnect();  //则关闭文件描述符
                return;
            default:
                break;
            }
        }
    }
    ~TcpClinet()
    {
    }

private:
    ConnectClient _connect;
};

void Usage(std::string args)
{
    std::cout << "Usage:\n\t" << args << " serverip serverport" << std::endl;
}
// ./Tcp_Client serverip serverport
int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        Usage(argv[0]);
        exit(1);
    }

    std::string serverip = argv[1];
    int serverport = std::stoi(argv[2]);

    std::shared_ptr<TcpClinet> TcpClientptr = std::make_shared<TcpClinet>(serverip, serverport);
    TcpClientptr->Excute();

    return 0;
}

相关推荐
祁白_1 小时前
[0xV01D]_Night Traffic_writeUp
网络·安全·ctf·writeup
xingyuzhisuan1 小时前
网络 Token 常见故障原理,基础排查科普
运维·服务器·网络·php
北京耐用通信1 小时前
自动化工程师必修课:耐达讯自动化Modbus TCP转PROFIBUS协议转换的核心逻辑与应用
人工智能·物联网·网络协议·自动化·信息与通信
学习3人组3 小时前
企业交换机OSPF路由协议配置与防护
网络
青风973 小时前
SDDGR:基于稳定扩散的深度生成重放,用于类增量对象检测(CVPR 2024)
网络·人工智能·深度学习·神经网络·计算机视觉
Chockmans4 小时前
春秋云境CVE-2020-21652(极速版)
计算机网络·安全·web安全·网络安全·安全威胁分析·春秋云境·cve-2020-21652
byte轻骑兵6 小时前
【HID】规范精讲[20]: 蓝牙HID低功耗黑科技——Sniff Subrating如何让设备续航翻倍?
网络·科技·人机交互·hid·蓝牙键盘
liana87446 小时前
私有化视频会议:把会议室“搬”进自己的网络
网络
Cat_Rocky7 小时前
Linux-ansible之Playbook简单应用
linux·网络·ansible
汤愈韬7 小时前
IPSec工作原理——TK
网络·网络协议·网络安全·security