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;
}
總結
通過本次客戶端斷線重連我們要理解我們不僅僅可以對我們的服務端進行設計我們的客戶端同樣如此比如客戶端的收發信息我們可以設計為多線程模式等等