客户端代码的编写
#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 下面调用connect
struct 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;
}
总结
通过本次客户端断线重连我们要理解我们不仅仅可以对我们的服务端进行设计我们的客户端同样如此比如客户端的收发信息我们可以设计为多线程模式等等
2747

被折叠的 条评论
为什么被折叠?



