connect的短线重连
- 客户端代码的编写
- 服务器代码的编写
- 总结
客户端代码的编写
#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>
#include <memory>
const int defaultconnectnum = 5;
const int defaultsockfd = -1;
const int defaultconnecttime = 1;
enum class Status // C++11 强类型枚举 会进行类型转换
{NEW, // 新建状态,就是单纯的连接CONNECTING, // 正在连接,仅仅方便查询conn状态CONNECTED, // 连接或者重连 成功DISCONNECTED, // 首次连接失败 进入重新连接CLOSED // 连接失败,经历重连,无法连接
};
class Connetion
{
public:Connetion(std::string ip, uint16_t port): _status(Status::NEW),_ip(ip),_port(port),_sockfd(defaultsockfd),_connect_num(defaultconnectnum),_connet_time(defaultconnecttime){}Status get_status(){return _status;}void Connect(){int n = socket(AF_INET, SOCK_STREAM, 0);if (n < 0){std::cerr << "socker failsure!!!" << std::endl;exit(1);}_sockfd = n;// 客户端我们不用进行我们的显示bind 下面调用connectstruct sockaddr_in serve;memset(&serve, 0, sizeof(serve));serve.sin_family = AF_INET;serve.sin_port = htons(_port);// 相对与 iner_aton线程安全inet_pton(AF_INET, _ip.c_str(), &serve.sin_addr);n = connect(_sockfd, (sockaddr *)&serve, sizeof(serve));if (n < 0){// 连接失败// 此时我们把我们的sockfd关闭Close();_status = Status::DISCONNECTED;}else{_status = Status::CONNECTED;}}void ReConnect(){int count = 0;while (true){_status = Status::CONNECTING;Connect();count++;if (_status == Status::CONNECTED){std::cout << "重连成功!!!" << std::endl;break;}if (count > _connect_num){_status = Status::CLOSED;break;}std::cout << "第" << count << "次重新连接!!!" << std::endl;sleep(_connet_time);}}// 连接成功进入通信模块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 << "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;}}}void Close(){if (_sockfd != -1){close(_sockfd);_status = Status::CLOSED;_sockfd = -1;}}private:Status _status;std::string _ip;uint16_t _port;int _sockfd;int _connect_num;int _connet_time;
};
class TcpClient
{
public:TcpClient(std::string ip, uint16_t port) : _ip(ip),_port(port),_conn(_ip, _port){}void Excute(){while (true){switch (_conn.get_status()){case Status::NEW:_conn.Connect();break;case Status::DISCONNECTED:_conn.ReConnect();break;case Status::CONNECTED:_conn.Process();break;case Status::CLOSED:_conn.Close();std::cout << "重连失败, 退出." << std::endl;return;default:break;}sleep(1);}}~TcpClient(){}private:std::string _ip;uint16_t _port;Connetion _conn;
};
void Usage(const std::string &process)
{std::cout << "Usage: " << process << " server_ip server_port" << std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);}uint16_t port = std::stoi(argv[2]);std::unique_ptr<TcpClient> client = std::make_unique<TcpClient>(argv[1], port);client->Excute();return 0;
}
服务器代码的编写
#include <iostream>
#include <cstring>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>const int BUFFER_SIZE = 1024;
const int PORT = 8081; // 默认端口int main() {// 1. 创建套接字int serverSocket = socket(AF_INET, SOCK_STREAM, 0);if (serverSocket < 0) {std::cerr << "Socket creation failed" << std::endl;return 1;}// 2. 设置SO_REUSEADDR选项int opt = 1;if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {std::cerr << "Set socket option failed" << std::endl;close(serverSocket);return 1;}// 3. 绑定地址sockaddr_in serverAddress;memset(&serverAddress, 0, sizeof(serverAddress));serverAddress.sin_family = AF_INET;serverAddress.sin_addr.s_addr = INADDR_ANY;serverAddress.sin_port = htons(PORT);if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) < 0) {std::cerr << "Bind failed" << std::endl;close(serverSocket);return 1;}// 4. 监听连接if (listen(serverSocket, 5) < 0) {std::cerr << "Listen failed" << std::endl;close(serverSocket);return 1;}std::cout << "Server listening on port " << PORT << "..." << std::endl;while (true) {// 5. 接受客户端连接sockaddr_in clientAddress;socklen_t clientAddrLen = sizeof(clientAddress);memset(&clientAddress, 0, sizeof(clientAddress));int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddrLen);if (clientSocket < 0) {std::cerr << "Accept failed" << std::endl;continue;}char clientIP[INET_ADDRSTRLEN];inet_ntop(AF_INET, &(clientAddress.sin_addr), clientIP, INET_ADDRSTRLEN);std::cout << "Client connected: " << clientIP << ":" << ntohs(clientAddress.sin_port) << std::endl;// 6. 处理客户端请求char buffer[BUFFER_SIZE];while (true) {ssize_t bytesRead = recv(clientSocket, buffer, BUFFER_SIZE - 1, 0);if (bytesRead <= 0) {if (bytesRead == 0) {std::cout << "Client disconnected" << std::endl;} else {std::cerr << "Recv error: " << strerror(errno) << std::endl;}break;}buffer[bytesRead] = '\0';std::cout << "Received: " << buffer << std::endl;// 原样发回客户端if (send(clientSocket, buffer, bytesRead, 0) < 0) {std::cerr << "Send failed: " << strerror(errno) << std::endl;break;}}// 7. 关闭客户端套接字close(clientSocket);std::cout << "Connection closed with " << clientIP << std::endl;}// 8. 关闭服务器套接字close(serverSocket);return 0;
}
总结
通过本次客户端断线重连我们要理解我们不仅仅可以对我们的服务端进行设计我们的客户端同样如此比如客户端的收发信息我们可以设计为多线程模式等等