简单实现能够断线重连的TCP客户端

代码 

主要是借助了状态机来实现断线重连

client.hpp

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

using namespace std;

#define RETRY_INTERVAL 1
#define MAX_RETRIES 5

void Usage(const std::string& process)
{
    cout << "Usage: " << process << "argv[1]: server_ip and argv[2]: server_port" << endl; 
}

enum ERR // 这里的出错状态码不用强枚举类型,因为exit()中必须传入整形,强枚举类型会编译报错
{
    SOCKFDERR = 1,
    BINDERR
};

enum class Status // 强枚举类型
{
    NEW,            // 新建立的链接,还未初始化完的状态
    CONNECTIGN,     // 正在连接
    CONNECTED,      // 连接成功状态,或者重连成功状态
    DISCONNECTED,   // 重连失败
    CLOSED          // 连接关闭状态
};

class Connection
{
    private:
        int _sockfd;
        uint16_t _serverport;   // 服务端的端口号
        string _serverip;       // 服务端的ip
        int  _retry_interval;   // 每次重连的时间间隔,单位是秒
        int _max_retries;       // 最大尝试重连次数
        Status _statu;          // 当前连接的状态
    public:
        Connection(uint16_t port,const string& ip) :
                    _sockfd(-1),_serverport(port),_serverip(ip),
                    _retry_interval(RETRY_INTERVAL),_max_retries(MAX_RETRIES),
                    _statu(Status::NEW) {}
        void Connect()
        {
            // 1.创建socket
            _sockfd = socket(AF_INET,SOCK_STREAM,0);
            if(_sockfd < 0)
            {
                cerr << "Create Sockfd Failed!" << endl;
                exit(SOCKFDERR);
            }

            // 2.connet,客户端是不需要bind的,因为在connet的时候,系统会给客户端随机分配端口号
            struct sockaddr_in server;
            memset(&server,0,sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port = htons(_serverport);
            // p:process(进程), n(网络)-- 不太准确,但是好记忆
            inet_pton(AF_INET,_serverip.c_str(),&server.sin_addr);
            int n = connect(_sockfd,(struct sockaddr*)&server,sizeof(server)); // 自动绑定端口
            if(n < 0)
            {
                Disconnect(); // 先重置连接的状态,关闭原来的套接字
                _statu = Status::DISCONNECTED; // 准备进行重连
                return;
            }
            // 连接成功了
            _statu = Status::CONNECTED;
        }
        int Sockfd() {return _sockfd; }
        // 重新连接
        void ReConnect()
        {
            // 其实就是不断调用Connect()
            int count = _max_retries;
            while(count > 0)
            {
                cout << "正在重新连接,剩余重连次数: " << count << " 次..." << endl;
                Connect(); // 重新连接
                count--;
                if(_statu == Status::CONNECTED) return; // 重连成功直接返回
                sleep(_retry_interval);
            }
            // 重连失败
            _statu = Status::CLOSED;
        }
        // 关闭连接
        void Disconnect()
        {
            if(_sockfd != -1)
            {
                close(_sockfd);
                _sockfd = -1;
                _statu = Status::CLOSED;
            }
        }
        Status GetStatu() { return _statu; }
        void Process()
        {
            // 简单的IO
            while(true)
            {
                string inbuffer;
                cout << "Please Enter# ";
                getline(cin,inbuffer);
                if(inbuffer.size() == 0) continue;
                ssize_t n = send(_sockfd,inbuffer.c_str(),sizeof(inbuffer),0);
                if(n < 0)
                {
                    cout << "Send n : " << n << "errno: " << errno << " errno string: " << strerror(errno) << endl;
                    _statu = Status::CLOSED;
                    break; 
                }
                else  
                {
                    char buffer[1024];
                    int m = recv(_sockfd,buffer,1023,0);
                    if(m < 0)
                    {
                        cout << "Recv m : " << "errno: " << errno << " errno string: " << strerror(errno) << endl;
                        _statu = Status::CLOSED;
                        break;
                    }
                    else if(m == 0)
                    {
                        _statu = Status::DISCONNECTED;
                        break;
                    }
                    else  
                    {
                        buffer[m] = 0;
                        cout << "Server said: " << endl;
                        cout << buffer << endl;
                    }
                }
            }
        }
        ~Connection() { Disconnect(); }
};

class TcpClient
{
    private:
        Connection _conn; 
    public:
        TcpClient(uint16_t port,const string& ip) : _conn(port,ip) 
        {}
        void Execute()
        {
            while(true)
            {
                switch(_conn.GetStatu())
                {
                    case Status::NEW: 
                        _conn.Connect();
                        break;
                    case Status::CONNECTED:
                        _conn.Process();
                        break;
                    case Status::DISCONNECTED:
                        _conn.ReConnect();
                        break;
                    case Status::CLOSED:
                        cout << "重连失败,关闭连接..." << endl;
                        _conn.Disconnect();
                        return;
                    default:
                        break;
                }
            }
        }
        ~TcpClient() {}
};

main.cc

#include "client.hpp"

int main(int argc,char* argv[])
{
    if(argc != 3)
    {
        Usage(argv[0]);
        return 1;
    }
    string serverip = argv[1];
    uint16_t serverport = stoi(argv[2]);
    TcpClient client(serverport,serverip);
    client.Execute();
    return 0;
}

makefile

TcpClient:main.cc
	g++ -std=c++11 $^ -o $@

.PHONY::clean
clean:
	rm -f TcpClient;

TCP是一种面向连接的可靠传输协议,通常在客户端服务器之间建立一条持久的连接。然而,由于网络环境的不稳定性或服务器故障等原因,客户端有时可能会断开与服务器的连接。为保持通信的连续性,客户端需要进行断线重连。 在TCP客户端断线后,重连的步骤如下: 1. 检测断线客户端首先需要检测与服务器的连接是否断开。一旦检测到断线客户端将无法通过连接发送或接收数据。 2. 关闭旧连接:客户端需要主动关闭旧的、断开的连接。这样可以释放资源并准备建立新的连接。 3. 重新建立连接:客户端会尝试重新建立连接。它会根据服务器地址和端口号,使用TCP握手协议与服务器建立连接。 4. 连接超时处理:如果客户端无法在一定时间内重新建立连接,可能是服务器未启动、网络故障或网络拥塞等原因。客户端需要进行连接超时处理,可以选择等待一段时间再重新尝试连接。 5. 连接成功后的处理:一旦客户端成功重新建立连接,它可以恢复之前的通信或重新发送之前未发送成功的数据。这样可以保证数据的完整性和连续性。 在进行断线重连时,需要注意以下几点: 1. 定时重连客户端可以定期尝试重连,避免频繁的连接尝试对服务器造成负载过大的影响。 2. 连接优化:客户端可以在重连时优化连接参数,例如调整超时时间、重试次数等。这样可以提高连接的成功率和重连的效率。 3. 异常处理:客户端在进行断线重连时,需要处理可能出现的异常情况。例如,当网络不稳定导致重连失败时,客户端需要进行错误处理,并通知用户或记录日志。 通过断线重连机制,TCP客户端可以在意外断开连接的情况下,及时恢复与服务器的连接,确保数据的正常传输。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值