好用的h5网站,烟台制作网站的公司,国家企业信用公示信息年报入口,怎么样开发小程序TcpServer 类的设计与实现#xff1a;构建高性能的 TCP 服务器
在现代网络编程中#xff0c;构建一个高效、稳定的 TCP 服务器是许多网络应用的基础。本文将详细介绍一个基于 C 的 TcpServer 类的设计与实现#xff0c;该类提供了创建 TCP 服务器、处理客户端连接、数据传输…TcpServer 类的设计与实现构建高性能的 TCP 服务器
在现代网络编程中构建一个高效、稳定的 TCP 服务器是许多网络应用的基础。本文将详细介绍一个基于 C 的 TcpServer 类的设计与实现该类提供了创建 TCP 服务器、处理客户端连接、数据传输和接收等功能。通过这个类我们可以更容易地理解和实现 TCP 通信的细节。
1. TcpServer 类概述
TcpServer 类是一个用于创建和管理 TCP 服务器的类。它封装了套接字创建、绑定、监听、客户端连接处理、数据发送和接收等操作使得网络通信更加简洁和易于管理。
2. 类构造与析构
构造函数 TcpServer::TcpServer(int _port) 和 TcpServer::TcpServer(std::string _host, int _port) 初始化服务器的主机地址和端口并创建套接字。
TcpServer::TcpServer(int _port) : TcpServer(0.0.0.0, _port) {
}TcpServer::TcpServer(std::string _host, int _port) : host(_host), port(_port) {std::cout create tcp server start. std::endl;socket_fd socket(AF_INET, SOCK_STREAM, 0);if (socket_fd -1) {std::cout socket create error!;return;}int ret set_epoll_mode(socket_fd, O_NONBLOCK);if (ret 0) {std::cout epoll_mode failed: ret std::endl;close(socket_fd);return;}server_addr.sin_family AF_INET;inet_pton(AF_INET, host.c_str(), server_addr.sin_addr);server_addr.sin_port htons(port);int opt 1;setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt));isCreate true;std::cout create tcp server ok. std::endl;
}析构函数 TcpServer::~TcpServer() 虚析构函数确保派生类的析构函数被正确调用。
TcpServer::~TcpServer() {
}3. 服务器启动与停止
启动服务器 TcpServer::Start() 绑定套接字到指定端口并开始监听。
void TcpServer::Start() {if (running || !isCreate) {std::cout TcpServer start failed! running running , port port std::endl;return;}auto ret bind(socket_fd, (struct sockaddr *)server_addr, sizeof(server_addr));if (ret -1) {std::cout bind faild: ret std::endl;close(socket_fd);return;}ret listen(socket_fd, SOMAXCONN);if (ret -1) {std::cout Listen failed: ret std::endl;close(socket_fd);return;}std::cout server open: host : port std::endl;epoll_start();
}停止服务器 TcpServer::Stop() 关闭服务器并释放资源。
void TcpServer::Stop() {this-Close();
}4. 资源管理
关闭连接 TcpServer::Close() 关闭套接字和 epoll 文件描述符释放资源。
void TcpServer::Close() {isCreate false;running false;socket_event.data.fd socket_fd;int ret epoll_ctl(epoll_fd, EPOLL_CTL_DEL, socket_fd, socket_event);ret close(socket_fd);std::cout socket_fd已关闭: ret std::endl;for (TcpClient *client : clients)client-Close();ret close(epoll_fd);std::cout TcpServer epoll_fd已关闭: ret std::endl;
}5. 非阻塞模式设置
设置非阻塞模式 TcpServer::set_epoll_mode() 设置套接字为非阻塞模式。
int TcpServer::set_epoll_mode(int sock_fd, int mode) {int flags fcntl(sock_fd, F_GETFL, 0);if (flags -1) {std::cout epoll_mode failed: sock_fd std::endl;return -1;}return fcntl(sock_fd, F_SETFL, flags | mode);
}6. 客户端连接处理
客户端接受线程 TcpServer::client_accept_thread() 处理客户端连接和数据事件。
void TcpServer::client_accept_thread() {struct epoll_event client_events[1024];while (running) {int ret epoll_wait(epoll_fd, client_events, 1024, -1);if (ret 0) {if (errno EAGAIN || errno EWOULDBLOCK)continue;else {std::cerr epoll_wait failed: ret : errno : strerror(errno) std::endl;break;}}for (int n 0; n ret; n) {if (client_events[n].data.fd socket_fd) {client_connect();} else {struct epoll_event client_event client_events[n];auto client std::find_if(clients.begin(), clients.end(), [client_event](const TcpClient *_client){ return (_client-client_fd client_event.data.fd); });if (client clients.end())continue;int ret (*client)-data_receive(*client);if (ret 0) {clients.erase(client);delete *client;}}}}std::cout 服务已关闭不再提供任何服务 std::endl;isDispose true;
}客户端连接 TcpServer::client_connect() 接受客户端连接并添加到 epoll 监控。
void TcpServer::client_connect() {struct sockaddr_in client_addr;socklen_t addr_len sizeof(client_addr);int client_fd accept(socket_fd, (struct sockaddr *)client_addr, addr_len);std::cout accept: client_fd : strerror(errno) std::endl;if (client_fd 0)return;TcpClient *client new TcpClient;client-running true;client-connected true;client-client_fd client_fd;client-local_addr client_addr;int ret set_epoll_mode(client_fd, O_NONBLOCK);if (ret -1) {std::cout 服务器接受客户端-set_epoll_mode failed: strerror(errno) std::endl;client-Close();return;}client-add_epoll_event(client_fd, epoll_fd, EPOLLIN | EPOLLET);client-DataReceived [this](void *sender, DataReceiveEventArgs *e){DataReceived.Invoke(this, e);};client-recv_data new char[client-recv_data_length];clients.push_back(client);std::cout 新的客户端已接入: inet_ntoa(client_addr.sin_addr) : htons(client_addr.sin_port) std::endl;
}7. epoll 事件处理
启动 epoll TcpServer::epoll_start() 创建 epoll 实例并添加监听套接字。
void TcpServer::epoll_start() {epoll_fd epoll_create1(0);if (epoll_fd -1) {std::cout poll_create1 failed: epoll_fd std::endl;close(socket_fd);return;}socket_event.events EPOLLIN;socket_event.data.fd socket_fd;int ret epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, socket_event);if (ret -1) {std::cout epoll_ctl failed: ret std::endl;close(epoll_fd);return;}running true;std::thread th std::thread(TcpServer::client_accept_thread, this);th.detach();
}完整的代码: TcpServer.h 头文件
#pragma once#include iostream
#include vector
#include list
#include string
#include sys/socket.h
#include netinet/in.h
#include sys/epoll.h
#include sys/fcntl.h
#include arpa/inet.h
#include algorithm
#include unistd.h
#include thread
#include DataReceiveEventArgs.h
#include TcpClient.hclass TcpServer
{
public:TcpServer(int _port);TcpServer(std::string _host, int _port);~TcpServer();public:EventHandlerDataReceiveEventArgs DataReceived;public:void Start();void Stop();void Close();bool IsDispose();private:void epoll_start(); // epoll初始化创建epollint set_epoll_mode(int sock_fd, int mode); // epoll模式--创建socket时为非阻塞模式void client_accept_thread();void client_connect();sockaddr_in get_remote_addr(int sock);private:int socket_fd;int epoll_fd;std::string host 0.0.0.0;int port 0;bool running false;bool isCreate false;int send_buff_size 1024 * 1024;int recv_buff_size 1024 * 1024;struct sockaddr_in server_addr;std::listTcpClient * clients;// 将监听套接字添加到 epoll 中监控 EPOLLIN 表示有数据可读事件struct epoll_event socket_event;bool isDispose false;
};TcpServer.cpp
#include TcpServer.hTcpServer::TcpServer(int _port) : TcpServer(0.0.0.0, _port)
{
}TcpServer::TcpServer(std::string _host, int _port) : host(_host), port(_port)
{std::cout create tcp server start. std::endl;// AF_INET 表示使用 IPv4 协议// SOCK_STREAM 表示套接字的类型表示 面向连接的流式套接字socket_fd socket(AF_INET, SOCK_STREAM, 0);// std::cout create tcp server socket_fd: socket_fd std::endl;if (socket_fd -1){std::cout socket ceate error!;return;}// 文件描述符为非阻塞模式int ret set_epoll_mode(socket_fd, O_NONBLOCK);if (ret 0){std::cout epoll_mode failed: ret std::endl;close(socket_fd);return;}server_addr.sin_family AF_INET;// ip字符串转intinet_pton(AF_INET, host.c_str(), server_addr.sin_addr);//server_addr.sin_addr.s_addr ntohl(server_addr.sin_addr.s_addr);server_addr.sin_port htons(port);// 以下设置表示当调用close关闭客户端时立即释放端口不等待// 在TCP服务端客户端调用 close(client_fd) 关闭连接后如果你尝试重新连接时出现端口没有完全释放的情况通常是由于 TCP 连接的 TIME_WAIT 状态没有及时清理。这是 TCP 协议的正常行为。// 在TCP连接关闭后端口会进入 TIME_WAIT 状态。这个状态的目的是确保最后的数据包能够正确到达。如果新的连接尝试在该端口上进行而该端口仍然处于 TIME_WAIT 状态就会出现端口被占用的情况。// TIME_WAIT 状态通常会持续一段时间默认是4分钟即240秒这可以通过操作系统的内核参数来修改。// struct linger linger_opt;// linger_opt.l_onoff 1; // 启用// linger_opt.l_linger 1; // 立即关闭// // 此方法亲测无效// // setsockopt(socket_fd, SOL_SOCKET, SO_LINGER, linger_opt, sizeof(linger_opt));int opt 1;setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt));isCreate true;std::cout create tcp server ok. std::endl;
}TcpServer::~TcpServer()
{
}void TcpServer::Start()
{if (running || !isCreate){std::cout TcpServer start failed! running running , port port std::endl;// close(socket_fd);return;}// 绑定套接字到指定端口auto ret bind(socket_fd, (struct sockaddr *)server_addr, sizeof(server_addr));if (ret -1){std::cout bind faild: ret std::endl;close(socket_fd);return;}// 开始监听ret listen(socket_fd, SOMAXCONN);if (ret -1){std::cout Listen failed: ret std::endl;close(socket_fd);return;}std::cout server open: host : port std::endl;epoll_start();
}void TcpServer::Stop()
{this-Close();
}void TcpServer::Close()
{isCreate false;running false;// 1. 删除socket_fd的epoll事件socket_event.data.fd socket_fd;int ret epoll_ctl(epoll_fd, EPOLL_CTL_DEL, socket_fd, socket_event);// 2. 关闭监听套接字ret close(socket_fd);std::cout socket_fd已关闭: ret std::endl;// 3. 从 epoll 中移除所有连接的客户端文件描述符for (TcpClient *client : clients)client-Close();// 4. 关闭 epoll 文件描述符ret close(epoll_fd);std::cout TcpServer epoll_fd已关闭: ret std::endl;
}bool TcpServer::IsDispose()
{return isDispose;
}int TcpServer::set_epoll_mode(int sock_fd, int mode)
{/*O_NONBLOCK非阻塞模式如果设置了这个标志表示该套接字或文件是非阻塞的执行读写操作时不会阻塞调用进程或线程。套接字在没有数据可读或可写时不会让程序等待而是立即返回。O_RDWR、O_WRONLY、O_RDONLY访问模式表示套接字的打开方式。O_APPEND追加模式指示文件或套接字在写操作时会追加数据。*/int flags fcntl(sock_fd, F_GETFL, 0); // 获取当前套接字的文件状态标志if (flags -1){std::cout epoll_mode failed: sock_fd std::endl;return -1;}// 设置套接字为非阻塞模式return fcntl(sock_fd, F_SETFL, flags | mode);
}void TcpServer::client_accept_thread()
{struct epoll_event client_events[1024];while (running){// 阻塞等待事件int ret epoll_wait(epoll_fd, client_events, 1024, -1);if (ret 0){if (errno EAGAIN || errno EWOULDBLOCK)continue;else{std::cerr epoll_wait failed: ret : errno : strerror(errno) std::endl;break;}}// 处理返回的事件for (int n 0; n ret; n){if (client_events[n].data.fd socket_fd){// 如果是监听套接字的事件,有新的客户端连接client_connect();}else{struct epoll_event client_event client_events[n];auto client std::find_if(clients.begin(), clients.end(), [client_event](const TcpClient *_client){ return (_client-client_fd client_event.data.fd); });if (client clients.end())continue;// 客户端有数据int ret (*client)-data_receive(*client);if (ret 0){clients.erase(client);delete *client;}}}}std::cout 服务已关闭不再提供任何服务 std::endl;isDispose true;
}void TcpServer::client_connect()
{struct sockaddr_in client_addr;socklen_t addr_len sizeof(client_addr);int client_fd accept(socket_fd, (struct sockaddr *)client_addr, addr_len); // 接受连接std::cout accept: client_fd : strerror(errno) std::endl;if (client_fd 0)return;TcpClient *client new TcpClient;client-running true;client-connected true;client-client_fd client_fd;client-local_addr client_addr;// 设置客户端套接字为非阻塞模式int ret set_epoll_mode(client_fd, O_NONBLOCK);if (ret -1){std::cout 服务器接受客户端-set_epoll_mode failed: strerror(errno) std::endl;client-Close();return;}// client.SetSendBuffSize(send_buff_size);// client.SetRecvBuffSize(recv_buff_size);// 将新客户端套接字添加到 epoll 中监听可读事件// client-create_epoll();client-add_epoll_event(client_fd, epoll_fd, EPOLLIN | EPOLLET);client-DataReceived [this](void *sender, DataReceiveEventArgs *e){DataReceived.Invoke(this, e);};// client-start_receive();client-recv_data new char[client-recv_data_length];clients.push_back(client);std::cout 新的客户端已接入: inet_ntoa(client_addr.sin_addr) : htons(client_addr.sin_port) std::endl;
}void TcpServer::epoll_start()
{// 创建epollepoll_fd epoll_create1(0);if (epoll_fd -1){std::cout poll_create1 failed: epoll_fd std::endl;close(socket_fd);return;}// 监听可读事件socket_event.events EPOLLIN;// 将监听套接字的文件描述符传给 epollsocket_event.data.fd socket_fd;// 将监听套接字添加到 epoll 中监控 EPOLLIN 事件表示有数据可读int ret epoll_ctl(epoll_fd, EPOLL_CTL_ADD, socket_fd, socket_event);if (ret -1){std::cout epoll_ctl failed: ret std::endl;close(epoll_fd);return;}running true;std::thread th std::thread(TcpServer::client_accept_thread, this);th.detach();
}
8. 总结
本文详细介绍了 TcpServer 类的设计与实现包括构造与析构、服务器启动与停止、资源管理、非阻塞模式设置、客户端连接处理以及 epoll 事件处理。通过这个类我们可以更容易地理解和实现 TCP 通信的细节。这个类提供了一个简洁的接口来管理 TCP 服务器使得网络编程更加高效和易于维护。
TcpServer 类的设计注重模块化和可扩展性允许开发者根据具体需求进行定制和扩展。通过使用 epoll 事件模型TcpServer 类能够支持高并发的客户端连接适用于需要处理大量并发连接的网络应用。此外类中的非阻塞模式设置和资源管理机制确保了服务器的稳定性和高效性。
总的来说TcpServer 类为构建高性能的 TCP 服务器提供了一个强大的基础。通过这个类开发者可以快速构建和部署 TCP 服务器满足各种网络应用的需求。