当前位置: 首页 > news >正文

connect 的断线重连

目录

TcpClient.cc

辅助函数

连接状态枚举

ClientConnection 类

成员变量

核心方法

构造函数:

Connect():

Disconnect():

SockFd():

Reconnect():

GetStatus():

Process():

析构函数:

TcpClient 类

核心方法

功能与流程

main 函数

程序工作流程

运行截图

重连成功

重连失败


客户端会面临服务器崩溃的情况,我们可以试着写一个客户端重连的代码,模拟并理解一些客户端行为,比如游戏客户端等

TcpClient.cc

#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>void Usage(const std::string &process)
{std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}enum class Status // C++11强类型枚举
{NEW,           // 新建状态,就是单纯的连接CONNECTING,    // 正在连接,仅仅方便查询conn状态CONNECTED,     // 连接或者重连成功DISTCONNECTED, // 重连失败CLOSED         // 连接失败,经历重连,无法连接
};class ClientConnection
{
public:ClientConnection(uint16_t serverport, const std::string serverip): _sockfd(-1), _serverport(serverport), _serverip(serverip), _retry_interval(1), _max_retries(5), _status(Status::NEW){}void Connect(){// 1.创建socket_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){std::cerr << "socket error" << std::endl;exit(1);}// 2.要不要bind?必须要有Ip和Port,需要bind,但是不需要用户显示的bind,client系统随机端口// 发起连接的时候,client会被OS自动进行本地绑定// 2.connectstruct 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);// 1.字符串ip->4字节IP 2.网络序列int n = connect(_sockfd, (struct sockaddr *)&server, sizeof(server)); // 自动进行bindif (n < 0){Disconnect();                    // 恢复sockfd的默认值,是连接没有成功,不代表sockfd创建没有成功_status = Status::DISTCONNECTED; // 没有连接成功return;}_status = Status::CONNECTED; // 连接成功}int SockFd(){return _sockfd;}void Reconnect(){_status = Status::CONNECTING; // 正在重连int count = 0;while (count < _max_retries){Connect(); // 重连if (_status == Status::CONNECTED){return;}sleep(_retry_interval);count++;std::cout << "重连次数: " << count << ", 最大上限: " << _max_retries << std::endl;}_status = Status::CLOSED; // 重连失败,可以关闭了}void Disconnect(){if (_sockfd != -1){close(_sockfd);_status = Status::CLOSED;_sockfd = -1;}}Status GetStatus(){return _status;}void Process(){// 简单的IO即可while (true){std::string inbuffer;std::cout << "Please Enter#";getline(std::cin, inbuffer);if (inbuffer.empty())continue;ssize_t n = write(_sockfd, inbuffer.c_str(), inbuffer.size());if (n > 0){char buffer[1024];ssize_t m = read(_sockfd, buffer, sizeof(buffer) - 1);if (m > 0){buffer[m] = 0;std::cout << buffer << std::endl;}else if (m == 0) // 这里证明server端掉线了{_status = Status::DISTCONNECTED;break;}else{std::cout << "read m : " << m << "errno: " << errno << "errno string: " << strerror(errno) << std::endl;_status = Status::CLOSED;break;}}else{std::cout << "write n : " << n << "errno: " << errno << "errno string: " << strerror(errno) << std::endl;_status = Status::CLOSED;break;}}}~ClientConnection(){Disconnect();}private:int _sockfd;uint16_t _serverport;  // server port端口号std::string _serverip; // server ip地址int _retry_interval;   // 重试时间间隔int _max_retries;      // 重试次数Status _status;        // 连接状态
};class TcpClient
{
public:TcpClient(uint16_t serverport, const std::string serverip): _conn(serverport, serverip){}void Execute(){while (true){switch (_conn.GetStatus()){case Status::NEW:_conn.Connect();break;case Status::CONNECTED:std::cout << "连接成功,开始进行通信." << std::endl;_conn.Process();break;case Status::DISTCONNECTED:std::cout << "连接失败或者对方掉线,开始重连." << std::endl;_conn.Reconnect();break;case Status::CLOSED:_conn.Disconnect();std::cout << "重连失败,退出." << std::endl;return;default:break;}}}~TcpClient(){}private:ClientConnection _conn; // 简单组合起来即可
};
// class Tcp//./tcpclient 127.0.0.1 8888
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);return 1;}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);TcpClient client(serverport, serverip);client.Execute();return 0;
}

我们一块一块来讲

辅助函数

这个函数用于提示用户程序的正确使用方法,当命令行参数不正确时会被调用。

连接状态枚举

使用 C++11 的强类型枚举定义了客户端连接的各种状态,使状态管理更清晰。

ClientConnection 类

这个类封装了与服务器连接的所有操作:

成员变量

核心方法

构造函数:

  • 作用:用于初始化ClientConnection类的成员变量。
  • 成员变量初始化
    • _sockfd初始化为 - 1,表示尚未创建 socket。
    • _serverport_serverip分别由构造函数参数初始化,对应服务器的端口和 IP 地址。
    • _retry_interval初始化为 1,即重连时间间隔为 1 秒。
    • _max_retries初始化为 5,即最大重连次数为 5 次。
    • _status初始化为Status::NEW,表示初始连接状态为新建。
Connect()

功能概述

该函数完成了 TCP 客户端与服务器建立连接的核心流程,包括创建 socket、设置服务器地址结构、发起连接并根据结果更新连接状态。

步骤解析

  1. 创建 socket
    • 调用socket函数创建基于 IPv4(AF_INET)的流式套接字(SOCK_STREAM,即 TCP 协议)。
    • 若创建失败,输出错误信息并退出程序。
  2. 设置服务器地址结构
    • 定义sockaddr_in类型的服务器地址变量server,并通过memset初始化。
    • 设置地址族为AF_INET,端口号(通过htons转换为网络字节序),以及服务器 IP 地址(通过inet_pton将字符串 IP 转换为网络字节序的 4 字节 IP)。
  3. 发起连接
    • 调用connect函数尝试与服务器建立连接。
    • 若连接失败,调用Disconnect函数恢复 socket 状态,并将连接状态设置为DISTCONNECTED(重连失败);若连接成功,将状态设置为CONNECTED
Disconnect()

功能与逻辑

  • 该函数用于安全地关闭客户端与服务器的 socket 连接。
  • 首先判断sockfd(socket 文件描述符)是否有效(不为 - 1),若有效则调用close函数关闭 socket。
  • 随后将连接状态_status设置为CLOSED,并将sockfd重置为 - 1,确保资源释放和状态一致性。
SockFd():

返回sockfd套接字

Reconnect()

功能与流程

  • 状态设置:首先将连接状态置为CONNECTING,表示正在重连。
  • 重连循环:通过while循环,在最大重试次数(_max_retries)内反复调用Connect函数尝试重连。
  • 重试控制:每次重连失败后,休眠_retry_interval时间(秒),并输出重连次数和最大上限的提示信息。
  • 结果处理:若重连成功(状态为CONNECTED)则直接返回;若达到最大重试次数仍失败,将状态置为CLOSED
GetStatus():

获取当前状态。

Process()

功能与流程

  • 输入处理:通过getline读取用户输入,若输入为空则跳过本次循环。
  • 数据发送:调用write函数将用户输入发送给服务器。
  • 数据接收:调用read函数接收服务器返回的数据,若接收成功则输出;若接收长度为 0(服务器端掉线),则将状态置为DISTCONNECTED并退出循环;若接收失败,输出错误信息并将状态置为CLOSED
  • 发送失败处理:若write失败,输出错误信息并将状态置为CLOSED
析构函数:

调用Disconnect使sockfd和status回到初始状态。

TcpClient 类

这个类是客户端的入口,通过组合 ClientConnection 对象来实现功能:

核心方法

Execute()

功能与流程
  • 循环控制:通过while(true)循环持续监控连接状态。
  • 状态分支
    • 当状态为NEW时,调用Connect尝试建立连接。
    • 当状态为CONNECTED时,输出连接成功提示并调用Process进行通信交互。
    • 当状态为DISTCONNECTED时,输出重连提示并调用Reconnect尝试重连。
    • 当状态为CLOSED时,调用Disconnect并输出重连失败提示后退出函数。

main 函数

  • 检查命令行参数
  • 解析服务器 IP 和端口
  • 创建 TcpClient 对象并调用 Execute () 开始运行

程序工作流程

  1. 程序启动时检查命令行参数
  2. 创建客户端对象,初始状态为 NEW
  3. 尝试连接服务器,成功则进入 CONNECTED 状态
  4. 在 CONNECTED 状态下,用户可以输入消息发送给服务器,并接收服务器响应
  5. 如果连接断开(服务器关闭或网络问题),进入 DISTCONNECTED 状态并尝试重连
  6. 重连失败达到最大次数后,进入 CLOSED 状态并退出程序

运行截图

重连成功

重连失败

http://www.dtcms.com/a/498866.html

相关文章:

  • wp-config.php文件是什么
  • 编译esp-idf小智报错
  • 微信小程序开发踩坑记:从AI工具翻车到找到合适方案
  • 《3D植被建模痛点解决:开放世界层级实例化+GPU批处理优化方案》
  • openharmony之分布式蓝牙实现多功能场景设备协同实战
  • Linux ARM 程序启动全链路解析:从 shell 到 main(含动态/静态链接)
  • 具身智能黑客松之旅002
  • 免费发布产品网站网站权重能带来什么作用
  • 碰一碰发视频 系统源码 /PHP 语言开发方案
  • 网站大学报名官网入口网站插件代码下载
  • Cors能干什么?为什么需要它?
  • 远程办公自由:rdesktop+cpolar让Windows桌面随身而行
  • 计算机网络(tcp_socket )
  • 【小白笔记】在编程中,如何将概念上的数据结构(比如“树”)转化为代码中具体的数据类型和对象
  • 【STM32项目开源】STM32单片机智能农业大棚控制系统
  • github开源笔记应用程序项目推荐-Joplin
  • 【Swift】LeetCode 438. 找到字符串中所有字母异位词
  • 【SoC】【W800】基于WM IoT SDK的环境搭建
  • BFS 与 DFS——力扣102.二叉树的层序遍历
  • 使用IOT-Tree的OPC UA Client连接器对接OPC UA Server获取数据到系统中
  • 优质网站建设在哪里wordpress分类目录名称
  • 专题一 之 【双指针】
  • 将Windows应用上架至Microsoft Store
  • 对LlamaFactory的一点见解
  • 紫金保险车险官方网站关键词优化营销
  • 大模型-智能体-【篇一:单智能体框架】
  • LLMs之MultiAgent:OpenAgents(创建AI智能体网络)的简介、安装和使用方法、案例应用之详细攻略
  • IDEA 中 Tomcat 部署 Java Web 项目(2)
  • [SCADE编译原理] 状态机到数据流的源到源翻译(2005)
  • 小迪安全v2023学习笔记(一百三十四讲)—— Windows权限提升篇数据库篇MySQLMSSQLOracle自动化项目