Linux网络connect断线重连
Linux网络connect断线重连
1. 设计思路
用一个 enum class Status
c++11 支持的强类型枚举,罗列所有连接可能的状态。
enum class Status // c++11 强类型枚举
{
NEW, // 新建状态,单纯的连接
CONNECTING, // 正在连接,仅用于查询conn状态
CONNECTED, // 连接或者重连成功
DISCONNECTED, // 重连失败
CLOSED // 连接关闭,经历重连n次无法连接
};
强类型枚举要求使用时必须带上类的作用域,如
Status::NEW
,能增加代码的可读性
在客户端的类中,设计 Execute()
执行连接所有可能的行为。当连接状态为 Status::NEW
时去进行连接, 当连接状态为 Status::CONNECTED
执行客户端的正常通信任务,当连接状态为 Status::DISCONNECTED
时,执行重连操作,当连接状态为 Status::CLOSED
重连失败,整个进程退出。
void Execute()
{
while (true)
{
switch (_conn.GetStatus())
{
case Status::NEW:
_conn.Connect();
break;
case Status::CONNECTED:
_conn.Process();
break;
case Status::DISCONNECTED:
std::cout << "连接失败或对方掉线,开始重连" << std::endl;
_conn.Reconnect();
break;
case Status::CLOSED:
_conn.Disconnect();
std::cout<<"重连失败,退出"<<std::endl;
return;
default:
break;
}
}
其他看完整代码。
2. 完整代码
#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <cstdlib>
#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, // 连接或者重连成功
DISCONNECTED, // 重连失败
CLOSED // 连接关闭,经历重连n次无法连接
};
class ClinetConnection
{
public:
ClinetConnection(uint16_t serverport, std::string serverip)
: _sockfd(-1),
_serverport(serverport),
_serverip(serverip),
_retrt_interval(1),
_max_retries(5),
_status(Status::NEW)
{
}
void Connect()
{
// 创建socket
_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (_sockfd < 0)
{
std::cerr << "sock error" << std::endl;
exit(1);
}
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(_serverport);
inet_pton(AF_INET, _serverip.c_str(), &server.sin_addr);
int n = connect(_sockfd, (struct sockaddr *)&server, sizeof(server));
if (n < 0)
{
Disconnect();
_status = Status::DISCONNECTED;
return;
}
_status = Status::CONNECTED;
}
Status GetStatus()
{
return _status;
}
void Disconnect()
{
if (_sockfd != 1)
{
close(_sockfd);
_status = Status::CLOSED;
_sockfd = -1;
}
}
void Reconnect()
{
_status = Status::CONNECTING;
int count = 0;
while (count < _max_retries)
{
Connect();
if (_status == Status::CONNECTED)
{
return;
}
sleep(_retrt_interval);
count++;
std::cout << "重连次数: " << count << ",最大上限: " << _max_retries << std::endl;
}
_status = Status::CLOSED; // 重连失败
}
void Process()
{
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 << "echo messsge -> " << buffer << std::endl;
}
else if (m == 0) // 这里证明server端掉线了
{
_status = Status::DISCONNECTED;
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;
}
}
}
private:
int _sockfd;
uint16_t _serverport;
std::string _serverip;
int _retrt_interval; // 重试时间间隔
int _max_retries; // 重试次数
Status _status; // 连接状态
};
class TcpClient
{
public:
TcpClient(uint16_t serverport, std::string &serverip) : _conn(serverport, serverip)
{
}
void Execute()
{
while (true)
{
switch (_conn.GetStatus())
{
case Status::NEW:
_conn.Connect();
break;
case Status::CONNECTED:
_conn.Process();
break;
case Status::DISCONNECTED:
std::cout << "连接失败或对方掉线,开始重连" << std::endl;
_conn.Reconnect();
break;
case Status::CLOSED:
_conn.Disconnect();
std::cout<<"重连失败,退出"<<std::endl;
return;
default:
break;
}
}
}
private:
ClinetConnection _conn;
};
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;
}
3. 效果演示
重连失败:
重连成功: