news 2026/6/21 3:18:12

基于C++的UDP网络通信系统设计与实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于C++的UDP网络通信系统设计与实现

基于C++的UDP网络通信系统设计与实现

前言

在网络编程领域,UDP(User Datagram Protocol,用户数据报协议)作为一种无连接的传输层协议,以其高效、低延迟的特性在实时性要求高的应用场景中占据重要地位。与TCP协议相比,UDP不需要建立连接,不保证数据包的顺序和可靠性,但正是这种"轻量级"特性使其在视频流、在线游戏、DNS查询等领域得到广泛应用。

本文将深入探讨如何从零开始构建一个完整的UDP通信系统,涵盖服务器端、客户端的设计与实现,包括套接字编程的核心概念、关键系统调用、错误处理机制以及实际应用中的注意事项。通过本文的学习,读者不仅能够掌握UDP网络编程的基本技能,还能深入理解网络通信的底层原理。

本文实现的UDP通信系统具有以下特点:

  • 完整的服务器/客户端架构
  • 详细的错误处理和日志记录
  • 可配置的服务器参数
  • 跨平台兼容性考虑
  • 丰富的代码示例和详细注释

一、UDP服务器UdpServer.hpp

1.1 基本框架设计

UDP服务器的设计需要遵循模块化、可扩展的原则。我们将服务器封装为一个类,包含初始化、运行和清理等基本功能。

#ifndefUDPSERVER_HPP#defineUDPSERVER_HPP#include<iostream>#include<string>#include<cstring>#include<cstdlib>#include<unistd.h>#include<arpa/inet.h>#include<sys/socket.h>#include<sys/types.h>#include<netinet/in.h>#include<thread>#include<vector>#include<memory>#include<atomic>#include<functional>#include"Log.hpp"classUdpServer{private:intport_;// 服务器端口intsockfd_;// 套接字描述符std::atomic<bool>is_running_;// 服务器运行状态structsockaddr_inserver_addr_;// 服务器地址结构structsockaddr_inclient_addr_;// 客户端地址结构socklen_t client_addr_len_;// 客户端地址长度// 服务器配置参数size_t buffer_size_;// 缓冲区大小inttimeout_sec_;// 接收超时时间(秒)inttimeout_usec_;// 接收超时时间(微秒)boolreuse_addr_;// 是否重用地址public:// 构造函数explicitUdpServer(intport=8080);// 析构函数~UdpServer();// 禁止拷贝构造和赋值UdpServer(constUdpServer&)=delete;UdpServer&operator=(constUdpServer&)=delete;// 初始化服务器boolInit();// 运行服务器voidRun();// 停止服务器voidStop();// 设置配置参数voidSetBufferSize(size_t size){buffer_size_=size;}voidSetTimeout(intsec,intusec=0){timeout_sec_=sec;timeout_usec_=usec;}voidSetReuseAddr(boolreuse){reuse_addr_=reuse;}private:// 创建套接字boolCreateSocket();// 绑定地址boolBindAddress();// 设置套接字选项boolSetSocketOptions();// 处理接收到的数据virtualvoidProcessData(constchar*data,ssize_t len,conststructsockaddr_in&client_addr);// 发送响应boolSendResponse(constchar*data,ssize_t len,conststructsockaddr_in&client_addr);// 清理资源voidCleanup();};#endif// UDPSERVER_HPP

1.2 初始化函数Init详解

初始化函数是服务器启动的第一步,它负责套接字创建、地址绑定和选项设置等关键操作。

boolUdpServer::Init(){// 1. 创建日志实例Logger::Instance().Init("udp_server.log",LogLevel::INFO);LOG_INFO("Starting UDP server initialization...");// 2. 创建套接字if(!CreateSocket()){LOG_ERROR("Failed to create socket");returnfalse;}// 3. 设置套接字选项if(!SetSocketOptions()){LOG_ERROR("Failed to set socket options");close(sockfd_);returnfalse;}// 4. 绑定地址if(!BindAddress()){LOG_ERROR("Failed to bind address");close(sockfd_);returnfalse;}// 5. 初始化客户端地址结构memset(&client_addr_,0,sizeof(client_addr_));client_addr_len_=sizeof(client_addr_);LOG_INFO("UDP server initialized successfully on port %d",port_);LOG_INFO("Buffer size: %zu bytes",buffer_size_);LOG_INFO("Timeout: %d seconds %d microseconds",timeout_sec_,timeout_usec_);returntrue;}boolUdpServer::CreateSocket(){// 使用AF_INET表示IPv4,SOCK_DGRAM表示UDP协议sockfd_=socket(AF_INET,SOCK_DGRAM,0);if(sockfd_<0){LOG_ERROR("Socket creation failed: %s",strerror(errno));returnfalse;}LOG_DEBUG("Socket created successfully, fd: %d",sockfd_);returntrue;}boolUdpServer::SetSocketOptions(){intoptval=1;// 设置地址重用选项,避免"Address already in use"错误if(reuse_addr_){if(setsockopt(sockfd_,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval))<0){LOG_WARN("Failed to set SO_REUSEADDR: %s",strerror(errno));// 注意:这不是致命错误,可以继续运行}else{LOG_DEBUG("SO_REUSEADDR set successfully");}}// 设置接收超时if(timeout_sec_>0||timeout_usec_>0){structtimevaltv;tv.tv_sec=timeout_sec_;tv.tv_usec=timeout_usec_;if(setsockopt(sockfd_,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv))<0){LOG_WARN("Failed to set receive timeout: %s",strerror(errno));}else{LOG_DEBUG("Receive timeout set to %ld.%06ld seconds",tv.tv_sec,tv.tv_usec);}}// 设置发送缓冲区大小intsend_buf_size=1024*1024;// 1MBif(setsockopt(sockfd_,SOL_SOCKET,SO_SNDBUF,&send_buf_size,sizeof(send_buf_size))<0){LOG_WARN("Failed to set send buffer size: %s",strerror(errno));}// 设置接收缓冲区大小intrecv_buf_size=1024*1024;// 1MBif(setsockopt(sockfd_,SOL_SOCKET,SO_RCVBUF,&recv_buf_size,sizeof(recv_buf_size))<0){LOG_WARN("Failed to set receive buffer size: %s",strerror(errno));}returntrue;}boolUdpServer::BindAddress(){// 初始化服务器地址结构memset(&server_addr_,0,sizeof(server_addr_));// 设置地址族为IPv4server_addr_.sin_family=AF_INET;// 设置端口,使用htons进行字节序转换server_addr_.sin_port=htons(port_);// 设置IP地址为INADDR_ANY,表示监听所有网络接口server_addr_.sin_addr.s_addr=htonl(INADDR_ANY);// 绑定套接字到指定地址和端口if(bind(sockfd_,(structsockaddr*)&server_addr_,sizeof(server_addr_))<0){LOG_ERROR("Bind failed on port %d: %s",port_,strerror(errno));returnfalse;}// 获取实际绑定的地址信息structsockaddr_inactual_addr;socklen_t actual_len=sizeof(actual_addr);if(getsockname(sockfd_,(structsockaddr*)&actual_addr,&actual_len)==0){charip_str[INET_ADDRSTRLEN];inet_ntop(AF_INET,&actual_addr.sin_addr,ip_str,sizeof(ip_str));LOG_INFO("Server bound to %s:%d",ip_str,ntohs(actual_addr.sin_port));}returntrue;}

1.3 关键系统调用详解

1.3.1 inet_addr函数

inet_addr函数用于将点分十进制表示的IPv4地址转换为网络字节序的32位整数。虽然本文代码中使用的是inet_pton(更安全的版本),但理解inet_addr仍然很重要。

// inet_addr的使用示例constchar*ip_str="192.168.1.100";in_addr_t addr=inet_addr(ip_str);if(addr==INADDR_NONE){LOG_ERROR("Invalid IP address: %s",ip_str);}else{LOG_DEBUG("IP %s converted to network byte order: 0x%08x",ip_str,addr);// 转换回点分十进制格式structin_addraddr_struct;addr_struct.s_addr=addr;char*ip_str_back=inet_ntoa(addr_struct);LOG_DEBUG("Converted back to string: %s",ip_str_back);}// 现代推荐使用inet_pton(更安全,支持IPv6)structsockaddr_inaddr;if(inet_pton(AF_INET,ip_str,&addr.sin_addr)<=0){LOG_ERROR("Invalid IP address format: %s",ip_str);}
1.3.2 bzero和memset函数

bzero是BSD系统中用于将内存区域清零的函数,而memset是标准C库函数,功能更通用。

// bzero的使用(传统方式)structsockaddr_inaddr;bzero(&addr,sizeof(addr));// 将整个结构体清零// memset的等效用法memset(&addr,0,sizeof(addr));// 更标准的做法// memset的更多用途charbuffer[1024];// 全部设置为0memset(buffer,0,sizeof(buffer));// 全部设置为特定值memset(buffer,'A',sizeof(buffer));// 部分设置memset(buffer,0,100);// 只清空前100字节// 性能比较:对于大内存块,memset通常经过优化,性能更好

1.4 服务器运行函数Run

Run函数是服务器的核心,负责循环接收客户端请求并处理。

voidUdpServer::Run(){if(sockfd_<0){LOG_ERROR("Cannot run server: socket not initialized");return;}is_running_=true;LOG_INFO("UDP server started, waiting for connections...");// 分配接收缓冲区std::vector<char>buffer(buffer_size_);// 主循环while(is_running_){// 重置客户端地址信息memset(&client_addr_,0,sizeof(client_addr_));client_addr_len_=sizeof(client_addr_);// 接收数据ssize_t recv_len=recvfrom(sockfd_,buffer.data(),buffer.size()-1,0,(structsockaddr*)&client_addr_,&client_addr_len_);if(recv_len<0){// 处理接收错误if(errno==EAGAIN||errno==EWOULDBLOCK){// 超时,继续循环continue;}elseif(errno==EINTR){// 被信号中断LOG_DEBUG("recvfrom interrupted by signal");continue;}else{LOG_ERROR("recvfrom failed: %s",strerror(errno));break;}}elseif(recv_len==0){// UDP中recvfrom返回0表示收到了0字节的数据包LOG_DEBUG("Received empty datagram");continue;}// 确保字符串以null结尾buffer[recv_len]='\0';// 获取客户端信息charclient_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET,&client_addr_.sin_addr,client_ip,sizeof(client_ip));uint16_tclient_port=ntohs(client_addr_.sin_port);LOG_DEBUG("Received %zd bytes from %s:%d",recv_len,client_ip,client_port);LOG_DEBUG("Data: %s",buffer.data());// 处理数据ProcessData(buffer.data(),recv_len,client_addr_);}LOG_INFO("UDP server stopped");Cleanup();}voidUdpServer::ProcessData(constchar*data,ssize_t len,conststructsockaddr_in&client_addr){// 默认实现:原样返回数据(echo服务器)LOG_DEBUG("Processing %zd bytes of data",len);// 构造响应std::string response="Server received: ";response.append(data,len);// 发送响应if(!SendResponse(response.c_str(),response.length(),client_addr)){LOG_ERROR("Failed to send response to client");}}boolUdpServer::SendResponse(constchar*data,ssize_t len,conststructsockaddr_in&client_addr){if(len<=0){LOG_WARN("Attempting to send empty data");returntrue;// 空数据发送"成功"}// 发送数据ssize_t sent_len=sendto(sockfd_,data,len,0,(conststructsockaddr*)&client_addr,sizeof(client_addr));if(sent_len<0){LOG_ERROR("sendto failed: %s",strerror(errno));returnfalse;}if(sent_len!=len){LOG_WARN("Partial send: %zd of %zd bytes sent",sent_len,len);}// 获取客户端信息用于日志charclient_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET,&client_addr.sin_addr,client_ip,sizeof(client_ip));uint16_tclient_port=ntohs(client_addr.sin_port);LOG_DEBUG("Sent %zd bytes to %s:%d",sent_len,client_ip,client_port);returntrue;}

1.5 recvfrom和sendto函数深度解析

1.5.1 recvfrom函数

recvfrom是UDP接收数据的核心函数,它不仅可以接收数据,还能获取发送者的地址信息。

/** * recvfrom函数原型: * ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, * struct sockaddr *src_addr, socklen_t *addrlen); * * 参数说明: * - sockfd: 套接字描述符 * - buf: 接收缓冲区 * - len: 缓冲区大小 * - flags: 标志位,常用值: * * 0: 默认行为 * * MSG_WAITALL: 等待所有数据(对UDP通常无效) * * MSG_DONTWAIT: 非阻塞模式 * * MSG_PEEK: 查看数据但不从缓冲区移除 * - src_addr: 发送方地址(输出参数) * - addrlen: 地址长度(输入输出参数) * * 返回值: * - 成功:接收到的字节数 * - 失败:-1,设置errno * - 连接关闭(TCP)或空数据包(UDP):0 */// recvfrom的完整示例voidReceiveExample(intsockfd){structsockaddr_inclient_addr;socklen_t addr_len=sizeof(client_addr);charbuffer[4096];// 设置接收超时structtimevaltv;tv.tv_sec=5;tv.tv_usec=0;setsockopt(sockfd,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));// 接收数据ssize_t recv_len=recvfrom(sockfd,buffer,sizeof(buffer)-1,MSG_DONTWAIT,// 非阻塞模式(structsockaddr*)&client_addr,&addr_len);if(recv_len>0){buffer[recv_len]='\0';// 获取客户端信息charip_str[INET_ADDRSTRLEN];inet_ntop(AF_INET,&client_addr.sin_addr,ip_str,sizeof(ip_str));uint16_tport=ntohs(client_addr.sin_port);LOG_INFO("Received from %s:%d: %s",ip_str,port,buffer);// 处理不同的消息类型ProcessMessage(buffer,recv_len,client_addr);}elseif(recv_len==0){LOG_DEBUG("Received empty datagram");}else{// 错误处理if(errno==EAGAIN||errno==EWOULDBLOCK){LOG_DEBUG("No data available (non-blocking)");}elseif(errno==EINTR){LOG_DEBUG("Interrupted by signal");}else{LOG_ERROR("Receive error: %s",strerror(errno));}}}// 处理不同类型的消息voidProcessMessage(constchar*data,ssize_t len,conststructsockaddr_in&client_addr){// 简单的协议处理示例if(len>=4&&strncmp(data,"PING",4)==0){LOG_DEBUG("Received PING request");SendResponse("PONG",4,client_addr);}elseif(len>=4&&strncmp(data,"TIME",4)==0){time_t now=time(nullptr);std::string time_str=ctime(&now);SendResponse(time_str.c_str(),time_str.length(),client_addr);}elseif(len>=7&&strncmp(data,"ECHO ",5)==0){// 回显消息内容SendResponse(data+5,len-5,client_addr);}else{std::string response="Unknown command: ";response.append(data,len);SendResponse(response.c_str(),response.length(),client_addr);}}
1.5.2 sendto函数

sendto是UDP发送数据的核心函数,用于向指定地址发送数据报。

/** * sendto函数原型: * ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, * const struct sockaddr *dest_addr, socklen_t addrlen); * * 参数说明: * - sockfd: 套接字描述符 * - buf: 发送缓冲区 * - len: 要发送的数据长度 * - flags: 标志位,常用值: * * 0: 默认行为 * * MSG_DONTWAIT: 非阻塞模式 * * MSG_CONFIRM: 确认路由有效(Linux特有) * * MSG_MORE: 还有更多数据要发送 * - dest_addr: 目标地址 * - addrlen: 地址长度 * * 返回值: * - 成功:发送的字节数(可能小于len) * - 失败:-1,设置errno */// sendto的完整示例boolSendData(intsockfd,constvoid*data,size_t len,conststructsockaddr_in&dest_addr){if(len==0){LOG_WARN("Attempting to send zero-length data");returntrue;}// 检查数据包大小(UDP最大约64KB,实际建议小于1500字节避免分片)if(len>65507){// 65535 - 20(IP头) - 8(UDP头)LOG_ERROR("Datagram too large: %zu bytes (max: 65507)",len);returnfalse;}if(len>1400){LOG_WARN("Large datagram: %zu bytes (may be fragmented)",len);}// 分块发送大数据(如果需要)constsize_t MAX_CHUNK=1400;// 避免IP分片的推荐大小size_t total_sent=0;while(total_sent<len){size_t chunk_size=std::min(MAX_CHUNK,len-total_sent);constchar*chunk_start=static_cast<constchar*>(data)+total_sent;ssize_t sent=sendto(sockfd,chunk_start,chunk_size,0,(conststructsockaddr*)&dest_addr,sizeof(dest_addr));if(sent<0){LOG_ERROR("Failed to send chunk: %s (sent %zu/%zu bytes)",strerror(errno),total_sent,len);returnfalse;}total_sent+=sent;// 添加小延迟避免拥塞if(chunk_size==MAX_CHUNK&&total_sent<len){usleep(1000);// 1ms延迟}}LOG_DEBUG("Successfully sent %zu bytes to %s:%d",total_sent,inet_ntoa(dest_addr.sin_addr),ntohs(dest_addr.sin_port));returntrue;}// 发送不同类型的消息voidSendVariousMessages(intsockfd,conststructsockaddr_in&dest_addr){// 1. 发送字符串constchar*text="Hello, UDP Server!";SendData(sockfd,text,strlen(text),dest_addr);// 2. 发送二进制数据structBinaryData{uint32_tmagic;uint16_tversion;uint8_ttype;uint8_tdata[256];}binary_msg;binary_msg.magic=htonl(0xDEADBEEF);binary_msg.version=htons(1);binary_msg.type=0x42;memset(binary_msg.data,0xAA,sizeof(binary_msg.data));SendData(sockfd,&binary_msg,sizeof(binary_msg),dest_addr);// 3. 发送结构化数据(JSON格式)std::string json_msg=R"({ "command": "update", "timestamp": )"+std::to_string(time(nullptr))+R"(, "data": {"temperature": 23.5, "humidity": 65.2} })";SendData(sockfd,json_msg.c_str(),json_msg.length(),dest_addr);// 4. 发送带序列号的消息for(inti=0;i<10;i++){std::string seq_msg="Message #"+std::to_string(i);SendData(sockfd,seq_msg.c_str(),seq_msg.length(),dest_addr);// 添加延迟usleep(100000);// 100ms}}

1.6 高级功能:多线程处理和连接管理

对于高性能UDP服务器,我们需要考虑多线程处理和客户端连接管理。

// 扩展UdpServer类,添加多线程支持classAdvancedUdpServer:publicUdpServer{private:std::vector<std::thread>worker_threads_;std::atomic<int>thread_count_;intmax_workers_;// 线程池和工作队列std::queue<std::pair<std::vector<char>,sockaddr_in>>task_queue_;std::mutex queue_mutex_;std::condition_variable queue_cv_;public:AdvancedUdpServer(intport=8080,intmax_workers=4):UdpServer(port),max_workers_(max_workers),thread_count_(0){}~AdvancedUdpServer(){Stop();}boolInit()override{if(!UdpServer::Init()){returnfalse;}// 创建工作线程for(inti=0;i<max_workers_;i++){worker_threads_.emplace_back(&AdvancedUdpServer::WorkerThread,this,i);}LOG_INFO("Started %d worker threads",max_workers_);returntrue;}voidRun()override{if(sockfd_<0){LOG_ERROR("Socket not initialized");return;}is_running_=true;LOG_INFO("Advanced UDP server started on port %d",port_);std::vector<char>buffer(buffer_size_);while(is_running_){structsockaddr_inclient_addr;socklen_t addr_len=sizeof(client_addr);// 接收数据ssize_t recv_len=recvfrom(sockfd_,buffer.data(),buffer.size()-1,0,(structsockaddr*)&client_addr,&addr_len);if(recv_len<0){if(errno==EAGAIN||errno==EWOULDBLOCK){continue;}elseif(errno==EINTR){continue;}else{LOG_ERROR("Receive error: %s",strerror(errno));break;}}if(recv_len>0){buffer[recv_len]='\0';// 将任务加入队列{std::lock_guard<std::mutex>lock(queue_mutex_);task_queue_.emplace(std::vector<char>(buffer.begin(),buffer.begin()+recv_len),client_addr);}// 通知工作线程queue_cv_.notify_one();// 获取统计信息if(task_queue_.size()>10){LOG_WARN("Task queue size: %zu",task_queue_.size());}}}// 通知所有工作线程退出queue_cv_.notify_all();// 等待所有线程结束for(auto&thread:worker_threads_){if(thread.joinable()){thread.join();}}LOG_INFO("Advanced UDP server stopped");Cleanup();}private:voidWorkerThread(intthread_id){thread_count_++;LOG_DEBUG("Worker thread %d started",thread_id);while(is_running_){std::pair<std::vector<char>,sockaddr_in>task;{std::unique_lock<std::mutex>lock(queue_mutex_);queue_cv_.wait(lock,[this](){return!task_queue_.empty()||!is_running_;});if(!is_running_&&task_queue_.empty()){break;}if(!task_queue_.empty()){task=std::move(task_queue_.front());task_queue_.pop();}else{continue;}}// 处理任务ProcessTask(task.first,task.second,thread_id);}thread_count_--;LOG_DEBUG("Worker thread %d stopped",thread_id);}voidProcessTask(conststd::vector<char>&data,constsockaddr_in&client_addr,intthread_id){// 获取客户端信息charclient_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET,&client_addr.sin_addr,client_ip,sizeof(client_ip));uint16_tclient_port=ntohs(client_addr.sin_port);LOG_DEBUG("Thread %d processing %zu bytes from %s:%d",thread_id,data.size(),client_ip,client_port);// 模拟处理时间std::this_thread::sleep_for(std::chrono::milliseconds(10));// 处理数据std::string response="Thread "+std::to_string(thread_id)+" processed: "+std::string(data.begin(),data.end());SendResponse(response.c_str(),response.length(),client_addr);}};// 连接管理类classConnectionManager{private:structClientInfo{sockaddr_in address;time_t last_activity;uint64_tpacket_count;uint64_ttotal_bytes;ClientInfo(constsockaddr_in&addr):address(addr),last_activity(time(nullptr)),packet_count(0),total_bytes(0){}};std::unordered_map<std::string,ClientInfo>clients_;std::mutex clients_mutex_;time_t cleanup_interval_;public:ConnectionManager(time_t cleanup_interval=300)// 5分钟:cleanup_interval_(cleanup_interval){}// 更新客户端活动voidUpdateClient(constsockaddr_in&addr,size_t bytes){std::string key=GetClientKey(addr);std::lock_guard<std::mutex>lock(clients_mutex_);autoit=clients_.find(key);if(it==clients_.end()){// 新客户端clients_.emplace(key,ClientInfo(addr));it=clients_.find(key);charip_str[INET_ADDRSTRLEN];inet_ntop(AF_INET,&addr.sin_addr,ip_str,sizeof(ip_str));LOG_INFO("New client connected: %s:%d",ip_str,ntohs(addr.sin_port));}// 更新统计信息it->second.last_activity=time(nullptr);it->second.packet_count++;it->second.total_bytes+=bytes;}// 清理不活跃的连接voidCleanupInactiveClients(){time_t now=time(nullptr);std::vector<std::string>to_remove;{std::lock_guard<std::mutex>lock(clients_mutex_);for(constauto&pair:clients_){if(now-pair.second.last_activity>cleanup_interval_){to_remove.push_back(pair.first);}}for(constauto&key:to_remove){constauto&client=clients_[key];charip_str[INET_ADDRSTRLEN];inet_ntop(AF_INET,&client.address.sin_addr,ip_str,sizeof(ip_str));LOG_INFO("Client %s:%d disconnected (inactive). ""Packets: %lu, Bytes: %lu",ip_str,ntohs(client.address.sin_port),client.packet_count,client.total_bytes);clients_.erase(key);}}if(!to_remove.empty()){LOG_INFO("Cleaned up %zu inactive clients",to_remove.size());}}// 获取客户端统计信息std::stringGetStats()const{std::lock_guard<std::mutex>lock(clients_mutex_);std::stringstream ss;ss<<"Active clients: "<<clients_.size()<<"\n";for(constauto&pair:clients_){charip_str[INET_ADDRSTRLEN];inet_ntop(AF_INET,&pair.second.address.sin_addr,ip_str,sizeof(ip_str));ss<<ip_str<<":"<<ntohs(pair.second.address.sin_port)<<" - Packets: "<<pair.second.packet_count<<", Bytes: "<<pair.second.total_bytes<<", Last activity: "<<(time(nullptr)-pair.second.last_activity)<<" seconds ago\n";}returnss.str();}private:std::stringGetClientKey(constsockaddr_in&addr)const{std::stringstream ss;ss<<inet_ntoa(addr.sin_addr)<<":"<<ntohs(addr.sin_port);returnss.str();}};

二、Main.cc实现

主程序负责初始化服务器并处理命令行参数。

#include<iostream>#include<csignal>#include<cstdlib>#include<memory>#include"UdpServer.hpp"#include"AdvancedUdpServer.hpp"// 全局服务器指针,用于信号处理std::unique_ptr<UdpServer>g_server;// 信号处理函数voidSignalHandler(intsignal){std::cout<<"\nReceived signal "<<signal<<", shutting down..."<<std::endl;if(g_server){g_server->Stop();}}// 显示使用帮助voidShowUsage(constchar*program_name){std::cout<<"UDP Server v1.0\n\n";std::cout<<"Usage: "<<program_name<<" [options]\n\n";std::cout<<"Options:\n";std::cout<<" -p, --port PORT Server port (default: 8080)\n";std::cout<<" -b, --buffer SIZE Buffer size in bytes (default: 4096)\n";std::cout<<" -t, --timeout SEC Receive timeout in seconds (default: 5)\n";std::cout<<" -w, --workers NUM Number of worker threads (default: 1)\n";std::cout<<" -a, --advanced Use advanced server with thread pool\n";std::cout<<" -h, --help Show this help message\n";std::cout<<"\nExamples:\n";std::cout<<" "<<program_name<<" -p 9000 -b 8192\n";std::cout<<" "<<program_name<<" --port 8080 --workers 4 --advanced\n";}// 解析命令行参数structServerConfig{intport=8080;size_t buffer_size=4096;inttimeout_sec=5;inttimeout_usec=0;intworkers=1;booladvanced=false;boolreuse_addr=true;};ServerConfigParseArguments(intargc,char*argv[]){ServerConfig config;for(inti=1;i<argc;i++){std::string arg=argv[i];if(arg=="-p"||arg=="--port"){if(i+1<argc){config.port=std::atoi(argv[++i]);if(config.port<=0||config.port>65535){std::cerr<<"Error: Port must be between 1 and 65535"<<std::endl;exit(1);}}}elseif(arg=="-b"||arg=="--buffer"){if(i+1<argc){config.buffer_size=std::atoi(argv[++i]);if(config.buffer_size<1024||config.buffer_size>65536){std::cerr<<"Error: Buffer size must be between 1024 and 65536"<<std::endl;exit(1);}}}elseif(arg=="-t"||arg=="--timeout"){if(i+1<argc){config.timeout_sec=std::atoi(argv[++i]);if(config.timeout_sec<0){std::cerr<<"Error: Timeout must be non-negative"<<std::endl;exit(1);}}}elseif(arg=="-w"||arg=="--workers"){if(i+1<argc){config.workers=std::atoi(argv[++i]);if(config.workers<1||config.workers>32){std::cerr<<"Error: Number of workers must be between 1 and 32"<<std::endl;exit(1);}}}elseif(arg=="-a"||arg=="--advanced"){config.advanced=true;}elseif(arg=="-h"||arg=="--help"){ShowUsage(argv[0]);exit(0);}elseif(arg=="--no-reuse"){config.reuse_addr=false;}else{std::cerr<<"Error: Unknown option '"<<arg<<"'"<<std::endl;ShowUsage(argv[0]);exit(1);}}returnconfig;}intmain(intargc,char*argv[]){// 解析命令行参数ServerConfig config=ParseArguments(argc,argv);// 注册信号处理signal(SIGINT,SignalHandler);signal(SIGTERM,SignalHandler);try{std::cout<<"=== UDP Server Starting ===\n";std::cout<<"Port: "<<config.port<<"\n";std::cout<<"Buffer size: "<<config.buffer_size<<" bytes\n";std::cout<<"Timeout: "<<config.timeout_sec<<" seconds\n";std::cout<<"Workers: "<<config.workers<<"\n";std::cout<<"Mode: "<<(config.advanced?"Advanced":"Basic")<<"\n";std::cout<<"===========================\n\n";// 创建服务器实例if(config.advanced){g_server=std::make_unique<AdvancedUdpServer>(config.port,config.workers);}else{g_server=std::make_unique<UdpServer>(config.port);}// 配置服务器g_server->SetBufferSize(config.buffer_size);g_server->SetTimeout(config.timeout_sec,config.timeout_usec);g_server->SetReuseAddr(config.reuse_addr);// 初始化服务器if(!g_server->Init()){std::cerr<<"Failed to initialize server"<<std::endl;return1;}std::cout<<"Server initialized successfully\n";std::cout<<"Press Ctrl+C to stop the server\n\n";// 运行服务器g_server->Run();}catch(conststd::exception&e){std::cerr<<"Exception: "<<e.what()<<std::endl;return1;}catch(...){std::cerr<<"Unknown exception occurred"<<std::endl;return1;}std::cout<<"\nServer stopped gracefully"<<std::endl;return0;}// 性能测试函数voidRunPerformanceTest(intport){std::cout<<"\n=== Performance Test ===\n";// 创建测试服务器autotest_server=std::make_unique<AdvancedUdpServer>(port,4);test_server->SetBufferSize(65536);test_server->SetTimeout(1,0);if(!test_server->Init()){std::cerr<<"Failed to initialize test server"<<std::endl;return;}// 在后台运行服务器std::threadserver_thread([&test_server](){test_server->Run();});// 给服务器时间启动std::this_thread::sleep_for(std::chrono::seconds(1));// 创建测试客户端intclient_sock=socket(AF_INET,SOCK_DGRAM,0);if(client_sock<0){std::cerr<<"Failed to create test client socket"<<std::endl;return;}structsockaddr_inserver_addr;memset(&server_addr,0,sizeof(server_addr));server_addr.sin_family=AF_INET;server_addr.sin_port=htons(port);server_addr.sin_addr.s_addr=htonl(INADDR_LOOPBACK);// 测试参数constintNUM_PACKETS=10000;constintPACKET_SIZE=1024;std::vector<char>test_data(PACKET_SIZE,'X');autostart_time=std::chrono::high_resolution_clock::now();// 发送测试数据包for(inti=0;i<NUM_PACKETS;i++){// 在数据中包含序列号memcpy(test_data.data(),&i,sizeof(i));ssize_t sent=sendto(client_sock,test_data.data(),PACKET_SIZE,0,(structsockaddr*)&server_addr,sizeof(server_addr));if(sent!=PACKET_SIZE){std::cerr<<"Failed to send packet "<<i<<std::endl;break;}// 每1000个包打印进度if((i+1)%1000==0){std::cout<<"Sent "<<(i+1)<<" packets..."<<std::endl;}// 小延迟避免拥塞usleep(10);}autoend_time=std::chrono::high_resolution_clock::now();autoduration=std::chrono::duration_cast<std::chrono::milliseconds>(end_time-start_time);close(client_sock);// 停止服务器test_server->Stop();if(server_thread.joinable()){server_thread.join();}// 输出结果std::cout<<"\nPerformance Test Results:\n";std::cout<<"Packets sent: "<<NUM_PACKETS<<"\n";std::cout<<"Packet size: "<<PACKET_SIZE<<" bytes\n";std::cout<<"Total data: "<<(NUM_PACKETS*PACKET_SIZE/1024.0/1024.0)<<" MB\n";std::cout<<"Total time: "<<duration.count()<<" ms\n";std::cout<<"Throughput: "<<(NUM_PACKETS*PACKET_SIZE*8.0/duration.count()/1000.0)<<" Mbps\n";std::cout<<"Packets per second: "<<(NUM_PACKETS*1000.0/duration.count())<<"\n";}

三、UDP客户端UdpClient.cc

3.1 基本框架设计

UDP客户端的设计需要简洁高效,支持多种操作模式。

#ifndefUDPCLIENT_H#defineUDPCLIENT_H#include<iostream>#include<string>#include<cstring>#include<cstdlib>#include<unistd.h>#include<arpa/inet.h>#include<sys/socket.h>#include<sys/types.h>#include<netinet/in.h>#include<vector>#include<chrono>#include<thread>#include<atomic>#include<memory>#include<iomanip>classUdpClient{private:intsockfd_;// 套接字描述符structsockaddr_inserver_addr_;// 服务器地址std::string server_ip_;// 服务器IP地址intserver_port_;// 服务器端口// 客户端状态std::atomic<bool>is_connected_;std::atomic<bool>is_running_;// 统计信息uint64_tpackets_sent_;uint64_tpackets_received_;uint64_tbytes_sent_;uint64_tbytes_received_;public:// 构造函数UdpClient(conststd::string&ip="127.0.0.1",intport=8080);// 析构函数~UdpClient();// 初始化客户端boolInit();// 连接服务器boolConnect();// 发送数据boolSend(conststd::string&data);boolSend(constvoid*data,size_t len);// 接收数据(阻塞)boolReceive(std::string&data,inttimeout_ms=5000);// 发送并等待响应boolSendAndReceive(conststd::string&send_data,std::string&recv_data,inttimeout_ms=5000);// 运行交互模式voidRunInteractive();// 运行性能测试模式voidRunPerformanceTest(intnum_packets=1000,intpacket_size=1024);// 获取统计信息voidGetStats(uint64_t&sent_packets,uint64_t&received_packets,uint64_t&sent_bytes,uint64_t&received_bytes)const;// 重置统计信息voidResetStats();// 断开连接voidDisconnect();private:// 创建套接字boolCreateSocket();// 设置套接字选项boolSetSocketOptions();// 打印状态voidPrintStatus()const;// 显示帮助信息voidShowHelp()const;};#endif// UDPCLIENT_H

3.2 创建套接字和连接

#include"UdpClient.h"UdpClient::UdpClient(conststd::string&ip,intport):server_ip_(ip),server_port_(port),sockfd_(-1),is_connected_(false),is_running_(false),packets_sent_(0),packets_received_(0),bytes_sent_(0),bytes_received_(0){// 初始化服务器地址结构memset(&server_addr_,0,sizeof(server_addr_));server_addr_.sin_family=AF_INET;server_addr_.sin_port=htons(server_port_);// 转换IP地址if(inet_pton(AF_INET,server_ip_.c_str(),&server_addr_.sin_addr)<=0){std::cerr<<"Invalid IP address: "<<server_ip_<<std::endl;}}UdpClient::~UdpClient(){Disconnect();}boolUdpClient::Init(){// 创建套接字if(!CreateSocket()){std::cerr<<"Failed to create socket"<<std::endl;returnfalse;}// 设置套接字选项if(!SetSocketOptions()){std::cerr<<"Failed to set socket options"<<std::endl;close(sockfd_);returnfalse;}std::cout<<"UDP client initialized"<<std::endl;std::cout<<"Server: "<<server_ip_<<":"<<server_port_<<std::endl;returntrue;}boolUdpClient::CreateSocket(){// 创建UDP套接字sockfd_=socket(AF_INET,SOCK_DGRAM,0);if(sockfd_<0){std::cerr<<"Socket creation failed: "<<strerror(errno)<<std::endl;returnfalse;}std::cout<<"Socket created successfully (fd: "<<sockfd_<<")"<<std::endl;returntrue;}boolUdpClient::SetSocketOptions(){intoptval=1;// 设置接收超时structtimevaltv;tv.tv_sec=5;tv.tv_usec=0;if(setsockopt(sockfd_,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv))<0){std::cerr<<"Failed to set receive timeout: "<<strerror(errno)<<std::endl;returnfalse;}// 启用广播(如果需要)optval=1;if(setsockopt(sockfd_,SOL_SOCKET,SO_BROADCAST,&optval,sizeof(optval))<0){std::cerr<<"Warning: Failed to enable broadcast: "<<strerror(errno)<<std::endl;}// 设置缓冲区大小intbuf_size=1024*1024;// 1MBif(setsockopt(sockfd_,SOL_SOCKET,SO_RCVBUF,&buf_size,sizeof(buf_size))<0){std::cerr<<"Warning: Failed to set receive buffer: "<<strerror(errno)<<std::endl;}if(setsockopt(sockfd_,SOL_SOCKET,SO_SNDBUF,&buf_size,sizeof(buf_size))<0){std::cerr<<"Warning: Failed to set send buffer: "<<strerror(errno)<<std::endl;}returntrue;}boolUdpClient::Connect(){if(sockfd_<0){std::cerr<<"Socket not initialized"<<std::endl;returnfalse;}// UDP是无连接的,这里只是测试与服务器的连通性std::string test_msg="CONNECT_TEST";std::string response;if(SendAndReceive(test_msg,response,3000)){std::cout<<"Successfully connected to server"<<std::endl;std::cout<<"Server response: "<<response<<std::endl;is_connected_=true;returntrue;}else{std::cerr<<"Failed to connect to server"<<std::endl;returnfalse;}}

3.3 发送和接收数据

boolUdpClient::Send(conststd::string&data){returnSend(data.c_str(),data.length());}boolUdpClient::Send(constvoid*data,size_t len){if(sockfd_<0){std::cerr<<"Socket not initialized"<<std::endl;returnfalse;}if(len==0){std::cerr<<"Attempting to send empty data"<<std::endl;returnfalse;}// 检查数据包大小if(len>65507){std::cerr<<"Data too large: "<<len<<" bytes (max: 65507)"<<std::endl;returnfalse;}// 发送数据ssize_t sent=sendto(sockfd_,data,len,0,(structsockaddr*)&server_addr_,sizeof(server_addr_));if(sent<0){std::cerr<<"Send failed: "<<strerror(errno)<<std::endl;returnfalse;}if(static_cast<size_t>(sent)!=len){std::cerr<<"Partial send: "<<sent<<" of "<<len<<" bytes"<<std::endl;}// 更新统计信息packets_sent_++;bytes_sent_+=sent;std::cout<<"Sent "<<sent<<" bytes to "<<server_ip_<<":"<<server_port_<<std::endl;returntrue;}boolUdpClient::Receive(std::string&data,inttimeout_ms){if(sockfd_<0){std::cerr<<"Socket not initialized"<<std::endl;returnfalse;}// 设置接收超时if(timeout_ms>0){structtimevaltv;tv.tv_sec=timeout_ms/1000;tv.tv_usec=(timeout_ms%1000)*1000;setsockopt(sockfd_,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));}// 接收缓冲区charbuffer[65536];structsockaddr_infrom_addr;socklen_t addr_len=sizeof(from_addr);// 接收数据ssize_t recv_len=recvfrom(sockfd_,buffer,sizeof(buffer)-1,0,(structsockaddr*)&from_addr,&addr_len);if(recv_len<0){if(errno==EAGAIN||errno==EWOULDBLOCK){std::cout<<"Receive timeout"<<std::endl;}else{std::cerr<<"Receive failed: "<<strerror(errno)<<std::endl;}returnfalse;}// 确保字符串以null结尾buffer[recv_len]='\0';data.assign(buffer,recv_len);// 获取发送者信息charfrom_ip[INET_ADDRSTRLEN];inet_ntop(AF_INET,&from_addr.sin_addr,from_ip,sizeof(from_ip));uint16_tfrom_port=ntohs(from_addr.sin_port);// 更新统计信息packets_received_++;bytes_received_+=recv_len;std::cout<<"Received "<<recv_len<<" bytes from "<<from_ip<<":"<<from_port<<std::endl;returntrue;}boolUdpClient::SendAndReceive(conststd::string&send_data,std::string&recv_data,inttimeout_ms){// 发送数据if(!Send(send_data)){returnfalse;}// 接收响应if(!Receive(recv_data,timeout_ms)){returnfalse;}returntrue;}

3.4 交互模式和性能测试

voidUdpClient::RunInteractive(){if(!is_connected_){if(!Connect()){std::cerr<<"Cannot start interactive mode: not connected"<<std::endl;return;}}is_running_=true;std::cout<<"\n=== UDP Client Interactive Mode ===\n";std::cout<<"Type 'help' for commands, 'quit' to exit\n\n";std::string input;while(is_running_){std::cout<<"udp> ";std::getline(std::cin,input);if(input.empty()){continue;}// 处理命令if(input=="quit"||input=="exit"){std::cout<<"Exiting..."<<std::endl;break;}elseif(input=="help"){ShowHelp();}elseif(input=="status"){PrintStatus();}elseif(input=="stats"){std::cout<<"\n=== Statistics ===\n";std::cout<<"Packets sent: "<<packets_sent_<<"\n";std::cout<<"Packets received: "<<packets_received_<<"\n";std::cout<<"Bytes sent: "<<bytes_sent_<<"\n";std::cout<<"Bytes received: "<<bytes_received_<<"\n";if(packets_sent_>0){std::cout<<"Average sent size: "<<(bytes_sent_/packets_sent_)<<" bytes\n";}if(packets_received_>0){std::cout<<"Average received size: "<<(bytes_received_/packets_received_)<<" bytes\n";}}elseif(input=="reset"){ResetStats();std::cout<<"Statistics reset"<<std::endl;}elseif(input=="ping"){std::string response;if(SendAndReceive("PING",response)){std::cout<<"Server response: "<<response<<std::endl;}}elseif(input=="time"){std::string response;if(SendAndReceive("TIME",response)){std::cout<<"Server time: "<<response;}}elseif(input.compare(0,4,"echo")==0){if(input.length()>5){std::string echo_data=input.substr(5);std::string response;if(SendAndReceive("ECHO "+echo_data,response)){std::cout<<"Echo: "<<response<<std::endl;}}else{std::cout<<"Usage: echo <message>"<<std::endl;}}elseif(input.compare(0,4,"file")==0){// 模拟文件传输std::string filename=input.length()>5?input.substr(5):"test.txt";std::cout<<"Simulating file transfer: "<<filename<<std::endl;// 创建模拟文件内容std::string file_content;for(inti=0;i<100;i++){file_content+="Line "+std::to_string(i+1)+": This is test data\n";}// 分块发送constsize_t CHUNK_SIZE=1024;size_t total_sent=0;intchunk_num=1;for(size_t i=0;i<file_content.length();i+=CHUNK_SIZE){size_t chunk_len=std::min(CHUNK_SIZE,file_content.length()-i);std::string chunk=file_content.substr(i,chunk_len);// 添加块头信息std::string chunk_with_header="FILE_CHUNK "+std::to_string(chunk_num)+" "+chunk;if(Send(chunk_with_header)){total_sent+=chunk_len;std::cout<<"Sent chunk "<<chunk_num<<" ("<<chunk_len<<" bytes)"<<std::endl;chunk_num++;// 小延迟usleep(10000);// 10ms}else{std::cerr<<"Failed to send chunk "<<chunk_num<<std::endl;break;}}std::cout<<"File transfer complete: "<<total_sent<<" bytes sent"<<std::endl;}elseif(input=="perftest"){RunPerformanceTest();}else{// 默认:发送原始消息std::string response;if(SendAndReceive(input,response)){std::cout<<"Response: "<<response<<std::endl;}}}}voidUdpClient::RunPerformanceTest(intnum_packets,intpacket_size){std::cout<<"\n=== Performance Test ===\n";std::cout<<"Packets: "<<num_packets<<"\n";std::cout<<"Packet size: "<<packet_size<<" bytes\n";std::cout<<"Total data: "<<(num_packets*packet_size/1024.0/1024.0)<<" MB\n\n";// 准备测试数据std::vector<char>test_data(packet_size,'X');// 记录开始时间autostart_time=std::chrono::high_resolution_clock::now();// 发送测试数据包intsuccessful_sends=0;for(inti=0;i<num_packets;i++){// 在数据中包含序列号和时间戳uint64_tseq=i;autotimestamp=std::chrono::high_resolution_clock::now();uint64_ttimestamp_ns=std::chrono::duration_cast<std::chrono::nanoseconds>(timestamp.time_since_epoch()).count();// 将序列号和时间戳复制到数据开始处memcpy(test_data.data(),&seq,sizeof(seq));memcpy(test_data.data()+sizeof(seq),&timestamp_ns,sizeof(timestamp_ns));if(Send(test_data.data(),packet_size)){successful_sends++;}// 每100个包打印进度if((i+1)%100==0){std::cout<<"Sent "<<(i+1)<<" packets..."<<std::endl;}// 控制发送速率(1000 packets/second)usleep(1000);}// 记录结束时间autoend_time=std::chrono::high_resolution_clock::now();autoduration=std::chrono::duration_cast<std::chrono::milliseconds>(end_time-start_time);// 接收响应(可选)std::cout<<"\nWaiting for responses..."<<std::endl;intresponses_received=0;autoreceive_start=std::chrono::high_resolution_clock::now();// 设置短超时接收剩余响应structtimevaltv;tv.tv_sec=2;tv.tv_usec=0;setsockopt(sockfd_,SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));charbuffer[65536];while(true){ssize_t recv_len=recvfrom(sockfd_,buffer,sizeof(buffer)-1,0,NULL,NULL);if(recv_len>0){responses_received++;// 解析响应中的序列号if(recv_len>=sizeof(uint64_t)){uint64_tseq;memcpy(&seq,buffer,sizeof(seq));// 可以在这里计算往返时间等}}else{break;// 超时}}autoreceive_end=std::chrono::high_resolution_clock::now();autoreceive_duration=std::chrono::duration_cast<std::chrono::milliseconds>(receive_end-receive_start);// 输出结果std::cout<<"\n=== Test Results ===\n";std::cout<<"Packets sent: "<<successful_sends<<"/"<<num_packets<<"\n";std::cout<<"Responses received: "<<responses_received<<"\n";std::cout<<"Send duration: "<<duration.count()<<" ms\n";std::cout<<"Receive duration: "<<receive_duration.count()<<" ms\n";if(duration.count()>0){doublesend_rate=(successful_sends*1000.0)/duration.count();doublethroughput=(successful_sends*packet_size*8.0)/(duration.count()*1000.0);// Mbpsstd::cout<<"Send rate: "<<send_rate<<" packets/second\n";std::cout<<"Throughput: "<<throughput<<" Mbps\n";}if(responses_received>0){doubleresponse_rate=(responses_received*1000.0)/receive_duration.count();std::cout<<"Response rate: "<<response_rate<<" packets/second\n";}doubleloss_rate=0;if(successful_sends>0){loss_rate=(1.0-(responses_received/(double)successful_sends))*100.0;std::cout<<"Packet loss rate: "<<std::fixed<<std::setprecision(2)<<loss_rate<<"%\n";}}voidUdpClient::ShowHelp()const{std::cout<<"\nAvailable commands:\n";std::cout<<" help Show this help message\n";std::cout<<" quit, exit Exit the client\n";std::cout<<" status Show connection status\n";std::cout<<" stats Show statistics\n";std::cout<<" reset Reset statistics\n";std::cout<<" ping Send ping to server\n";std::cout<<" time Get server time\n";std::cout<<" echo <message> Echo message to server\n";std::cout<<" file [name] Simulate file transfer\n";std::cout<<" perftest Run performance test\n";std::cout<<" <any text> Send custom message\n";}voidUdpClient::PrintStatus()const{std::cout<<"\n=== Client Status ===\n";std::cout<<"Server: "<<server_ip_<<":"<<server_port_<<"\n";std::cout<<"Socket: "<<(sockfd_>=0?"OK":"Not initialized")<<"\n";std::cout<<"Connected: "<<(is_connected_?"Yes":"No")<<"\n";std::cout<<"Running: "<<(is_running_?"Yes":"No")<<"\n";}voidUdpClient::GetStats(uint64_t&sent_packets,uint64_t&received_packets,uint64_t&sent_bytes,uint64_t&received_bytes)const{sent_packets=packets_sent_;received_packets=packets_received_;sent_bytes=bytes_sent_;received_bytes=bytes_received_;}voidUdpClient::ResetStats(){packets_sent_=0;packets_received_=0;bytes_sent_=0;bytes_received_=0;}voidUdpClient::Disconnect(){if(sockfd_>=0){// 发送断开连接消息std::string disconnect_msg="DISCONNECT";Send(disconnect_msg);// 关闭套接字close(sockfd_);sockfd_=-1;std::cout<<"Disconnected from server"<<std::endl;}is_connected_=false;is_running_=false;}// 客户端主程序intmain(intargc,char*argv[]){std::string server_ip="127.0.0.1";intserver_port=8080;// 解析命令行参数for(inti=1;i<argc;i++){std::string arg=argv[i];if(arg=="-s"||arg=="--server"){if(i+1<argc){server_ip=argv[++i];}}elseif(arg=="-p"||arg=="--port"){if(i+1<argc){server_port=std::atoi(argv[++i]);}}elseif(arg=="-h"||arg=="--help"){std::cout<<"UDP Client Usage:\n";std::cout<<" -s, --server IP Server IP address (default: 127.0.0.1)\n";std::cout<<" -p, --port PORT Server port (default: 8080)\n";std::cout<<" -t, --test Run performance test\n";std::cout<<" -i, --interactive Run in interactive mode\n";std::cout<<" -h, --help Show this help\n";return0;}elseif(arg=="-t"||arg=="--test"){// 性能测试模式UdpClientclient(server_ip,server_port);if(client.Init()&&client.Connect()){client.RunPerformanceTest();}return0;}elseif(arg=="-i"||arg=="--interactive"){// 交互模式(默认)}}std::cout<<"=== UDP Client ===\n";std::cout<<"Connecting to "<<server_ip<<":"<<server_port<<"\n\n";UdpClientclient(server_ip,server_port);if(!client.Init()){std::cerr<<"Failed to initialize client"<<std::endl;return1;}if(!client.Connect()){std::cerr<<"Failed to connect to server"<<std::endl;return1;}// 运行交互模式client.RunInteractive();std::cout<<"\nClient terminated"<<std::endl;return0;}

四、测试

4.1 单元测试

// TestUdpServer.cpp#include<gtest/gtest.h>#include<thread>#include<chrono>#include"UdpServer.hpp"#include"UdpClient.h"classUdpServerTest:public::testing::Test{protected:voidSetUp()override{// 启动测试服务器test_port_=9999;server_=std::make_unique<UdpServer>(test_port_);ASSERT_TRUE(server_->Init());// 在后台线程运行服务器server_thread_=std::thread([this](){server_->Run();});// 等待服务器启动std::this_thread::sleep_for(std::chrono::milliseconds(100));}voidTearDown()override{if(server_){server_->Stop();}if(server_thread_.joinable()){server_thread_.join();}}inttest_port_;std::unique_ptr<UdpServer>server_;std::thread server_thread_;};TEST_F(UdpServerTest,BasicEcho){UdpClientclient("127.0.0.1",test_port_);ASSERT_TRUE(client.Init());std::string send_data="Hello, Server!";std::string recv_data;EXPECT_TRUE(client.SendAndReceive(send_data,recv_data));EXPECT_NE(recv_data.find("Server received"),std::string::npos);EXPECT_NE(recv_data.find(send_data),std::string::npos);}TEST_F(UdpServerTest,MultipleClients){constintNUM_CLIENTS=5;std::vector<std::unique_ptr<UdpClient>>clients;std::vector<std::thread>client_threads;for(inti=0;i<NUM_CLIENTS;i++){autoclient=std::make_unique<UdpClient>("127.0.0.1",test_port_);ASSERT_TRUE(client->Init());clients.push_back(std::move(client));}// 并发发送消息for(inti=0;i<NUM_CLIENTS;i++){client_threads.emplace_back([&clients,i](){std::string send_data="Message from client "+std::to_string(i);std::string recv_data;EXPECT_TRUE(clients[i]->SendAndReceive(send_data,recv_data,3000));});}// 等待所有线程完成for(auto&thread:client_threads){thread.join();}}TEST_F(UdpServerTest,LargeData){UdpClientclient("127.0.0.1",test_port_);ASSERT_TRUE(client.Init());// 发送较大数据(小于64KB)std::stringlarge_data(50000,'X');// 50KBstd::string recv_data;EXPECT_TRUE(client.SendAndReceive(large_data,recv_data,5000));EXPECT_GT(recv_data.size(),large_data.size());}TEST_F(UdpServerTest,Performance){UdpClientclient("127.0.0.1",test_port_);ASSERT_TRUE(client.Init());constintNUM_PACKETS=100;constintPACKET_SIZE=1400;// 避免分片autostart_time=std::chrono::high_resolution_clock::now();intsuccess_count=0;for(inti=0;i<NUM_PACKETS;i++){std::stringdata(PACKET_SIZE,'A'+(i%26));std::string response;if(client.SendAndReceive(data,response,1000)){success_count++;}std::this_thread::sleep_for(std::chrono::milliseconds(10));}autoend_time=std::chrono::high_resolution_clock::now();autoduration=std::chrono::duration_cast<std::chrono::milliseconds>(end_time-start_time);std::cout<<"\nPerformance Test Results:\n";std::cout<<"Successful exchanges: "<<success_count<<"/"<<NUM_PACKETS<<"\n";std::cout<<"Total time: "<<duration.count()<<" ms\n";std::cout<<"Average RTT: "<<(duration.count()/(double)success_count)<<" ms\n";EXPECT_GT(success_count,NUM_PACKETS*0.9);// 90%成功率}intmain(intargc,char**argv){::testing::InitGoogleTest(&argc,argv);returnRUN_ALL_TESTS();}

4.2 集成测试

// IntegrationTest.cpp#include<iostream>#include<thread>#include<vector>#include<atomic>#include"AdvancedUdpServer.hpp"#include"UdpClient.h"classIntegrationTest{private:std::unique_ptr<AdvancedUdpServer>server_;std::thread server_thread_;intserver_port_;public:IntegrationTest(intport=8888,intworkers=4):server_port_(port){// 启动高性能服务器server_=std::make_unique<AdvancedUdpServer>(port,workers);server_->SetBufferSize(65536);server_->SetTimeout(1,0);if(!server_->Init()){throwstd::runtime_error("Failed to initialize server");}// 在后台运行服务器server_thread_=std::thread([this](){server_->Run();});// 等待服务器启动std::this_thread::sleep_for(std::chrono::milliseconds(200));std::cout<<"Test server started on port "<<port<<std::endl;}~IntegrationTest(){if(server_){server_->Stop();}if(server_thread_.joinable()){server_thread_.join();}std::cout<<"Test server stopped"<<std::endl;}voidRunLoadTest(intnum_clients,intmessages_per_client){std::cout<<"\n=== Load Test ===\n";std::cout<<"Clients: "<<num_clients<<"\n";std::cout<<"Messages per client: "<<messages_per_client<<"\n";std::vector<std::thread>client_threads;std::atomic<int>total_success{0};std::atomic<int>total_failures{0};autostart_time=std::chrono::high_resolution_clock::now();// 创建客户端线程for(intclient_id=0;client_id<num_clients;client_id++){client_threads.emplace_back([this,client_id,messages_per_client,&total_success,&total_failures](){UdpClientclient("127.0.0.1",server_port_);if(!client.Init()){total_failures+=messages_per_client;return;}intlocal_success=0;intlocal_failures=0;for(intmsg_num=0;msg_num<messages_per_client;msg_num++){std::string message="Client"+std::to_string(client_id)+"_Msg"+std::to_string(msg_num);std::string response;if(client.SendAndReceive(message,response,1000)){local_success++;// 验证响应包含原始消息if(response.find(message)==std::string::npos){std::cerr<<"Warning: Invalid response from server"<<std::endl;}}else{local_failures++;}// 小延迟避免拥塞std::this_thread::sleep_for(std::chrono::milliseconds(10));}total_success+=local_success;total_failures+=local_failures;std::cout<<"Client "<<client_id<<": "<<local_success<<"/"<<messages_per_client<<" successful"<<std::endl;});}// 等待所有客户端完成for(auto&thread:client_threads){thread.join();}autoend_time=std::chrono::high_resolution_clock::now();autoduration=std::chrono::duration_cast<std::chrono::milliseconds>(end_time-start_time);// 输出结果std::cout<<"\n=== Load Test Results ===\n";std::cout<<"Total messages: "<<(num_clients*messages_per_client)<<"\n";std::cout<<"Successful: "<<total_success<<"\n";std::cout<<"Failed: "<<total_failures<<"\n";std::cout<<"Success rate: "<<(total_success*100.0/(num_clients*messages_per_client))<<"%\n";std::cout<<"Total time: "<<duration.count()<<" ms\n";std::cout<<"Throughput: "<<(total_success*1000.0/duration.count())<<" messages/second\n";// 验证至少95%成功率doublesuccess_rate=total_success*100.0/(num_clients*messages_per_client);if(success_rate>=95.0){std::cout<<"\n✓ Load test PASSED"<<std::endl;}else{std::cout<<"\n✗ Load test FAILED (success rate below 95%)"<<std::endl;}}voidRunStressTest(){std::cout<<"\n=== Stress Test ===\n";constintNUM_CLIENTS=10;constintMESSAGES_PER_CLIENT=1000;constintMESSAGE_SIZE=1000;// 1KBstd::vector<std::thread>client_threads;std::atomic<uint64_t>total_bytes_sent{0};std::atomic<uint64_t>total_bytes_received{0};autostart_time=std::chrono::high_resolution_clock::now();for(inti=0;i<NUM_CLIENTS;i++){client_threads.emplace_back([this,i,&total_bytes_sent,&total_bytes_received](){UdpClientclient("127.0.0.1",server_port_);if(!client.Init()){return;}// 准备大消息std::stringlarge_message(MESSAGE_SIZE,'A'+(i%26));uint64_tclient_bytes_sent=0;uint64_tclient_bytes_received=0;for(intj=0;j<MESSAGES_PER_CLIENT;j++){// 修改消息内容large_message[0]='0'+(j%10);std::string response;if(client.SendAndReceive(large_message,response,500)){client_bytes_sent+=large_message.size();client_bytes_received+=response.size();}// 更短的延迟以增加压力if(j%100==0){std::this_thread::sleep_for(std::chrono::microseconds(100));}}total_bytes_sent+=client_bytes_sent;total_bytes_received+=client_bytes_received;std::cout<<"Stress client "<<i<<" completed"<<std::endl;});}for(auto&thread:client_threads){thread.join();}autoend_time=std::chrono::high_resolution_clock::now();autoduration=std::chrono::duration_cast<std::chrono::milliseconds>(end_time-start_time);std::cout<<"\n=== Stress Test Results ===\n";std::cout<<"Total data sent: "<<(total_bytes_sent/1024.0/1024.0)<<" MB\n";std::cout<<"Total data received: "<<(total_bytes_received/1024.0/1024.0)<<" MB\n";std::cout<<"Total time: "<<duration.count()<<" ms\n";if(duration.count()>0){doublesend_throughput=(total_bytes_sent*8.0)/(duration.count()*1000.0);// Mbpsdoublereceive_throughput=(total_bytes_received*8.0)/(duration.count()*1000.0);// Mbpsstd::cout<<"Send throughput: "<<send_throughput<<" Mbps\n";std::cout<<"Receive throughput: "<<receive_throughput<<" Mbps\n";std::cout<<"Total throughput: "<<(send_throughput+receive_throughput)<<" Mbps\n";}if(total_bytes_sent>0){std::cout<<"\n✓ Stress test completed successfully"<<std::endl;}}};intmain(){try{std::cout<<"=== UDP System Integration Test ===\n\n";IntegrationTesttest(8888,4);// 运行负载测试test.RunLoadTest(5,100);// 运行压力测试test.RunStressTest();std::cout<<"\n=== All tests completed ===\n";}catch(conststd::exception&e){std::cerr<<"Test failed: "<<e.what()<<std::endl;return1;}return0;}

4.3 网络测试工具

// NetworkTestTool.cpp#include<iostream>#include<iomanip>#include<vector>#include<map>#include<cmath>#include"UdpClient.h"classNetworkTestTool{private:std::string server_ip_;intserver_port_;public:NetworkTestTool(conststd::string&ip,intport):server_ip_(ip),server_port_(port){}voidRunLatencyTest(intnum_packets=100){std::cout<<"\n=== Latency Test ===\n";std::cout<<"Server: "<<server_ip_<<":"<<server_port_<<"\n";std::cout<<"Packets: "<<num_packets<<"\n\n";UdpClientclient(server_ip_,server_port_);if(!client.Init()){std::cerr<<"Failed to initialize client"<<std::endl;return;}std::vector<double>latencies;intsuccessful_packets=0;for(inti=0;i<num_packets;i++){// 准备包含时间戳的消息autosend_time=std::chrono::high_resolution_clock::now();uint64_tsend_ns=std::chrono::duration_cast<std::chrono::nanoseconds>(send_time.time_since_epoch()).count();std::string message="PING_"+std::to_string(i)+"_"+std::to_string(send_ns);std::string response;if(client.SendAndReceive(message,response,1000)){autorecv_time=std::chrono::high_resolution_clock::now();uint64_trecv_ns=std::chrono::duration_cast<std::chrono::nanoseconds>(recv_time.time_since_epoch()).count();// 计算往返时间doublertt_ns=static_cast<double>(recv_ns-send_ns);doublertt_ms=rtt_ns/1000000.0;latencies.push_back(rtt_ms);successful_packets++;if((i+1)%10==0){std::cout<<"Sent "<<(i+1)<<" packets..."<<std::endl;}}else{std::cout<<"Packet "<<i<<" lost"<<std::endl;}// 等待以避免拥塞std::this_thread::sleep_for(std::chrono::milliseconds(100));}// 计算统计信息if(!latencies.empty()){doublesum=0;doublemin_latency=latencies[0];doublemax_latency=latencies[0];for(doublelatency:latencies){sum+=latency;min_latency=std::min(min_latency,latency);max_latency=std::max(max_latency,latency);}doubleaverage=sum/latencies.size();// 计算标准差doublevariance=0;for(doublelatency:latencies){variance+=(latency-average)*(latency-average);}variance/=latencies.size();doublestddev=std::sqrt(variance);// 计算百分位数std::sort(latencies.begin(),latencies.end());doublep50=latencies[latencies.size()*0.5];doublep90=latencies[latencies.size()*0.9];doublep95=latencies[latencies.size()*0.95];doublep99=latencies[latencies.size()*0.99];// 输出结果std::cout<<"\n=== Latency Test Results ===\n";std::cout<<"Packets sent: "<<num_packets<<"\n";std::cout<<"Packets received: "<<successful_packets<<"\n";std::cout<<"Packet loss: "<<std::fixed<<std::setprecision(2)<<((num_packets-successful_packets)*100.0/num_packets)<<"%\n";std::cout<<"\nLatency statistics (ms):\n";std::cout<<" Minimum: "<<std::fixed<<std::setprecision(3)<<min_latency<<"\n";std::cout<<" Maximum: "<<max_latency<<"\n";std::cout<<" Average: "<<average<<"\n";std::cout<<" Std Dev: "<<stddev<<"\n";std::cout<<" 50th percentile: "<<p50<<"\n";std::cout<<" 90th percentile: "<<p90<<"\n";std::cout<<" 95th percentile: "<<p95<<"\n";std::cout<<" 99th percentile: "<<p99<<"\n";// 显示直方图DisplayHistogram(latencies);}}voidDisplayHistogram(conststd::vector<double>&data){if(data.empty())return;doublemin_val=*std::min_element(data.begin(),data.end());doublemax_val=*std::max_element(data.begin(),data.end());constintNUM_BINS=10;doublebin_width=(max_val-min_val)/NUM_BINS;std::vector<int>bins(NUM_BINS,0);for(doublevalue:data){intbin_index=static_cast<int>((value-min_val)/bin_width);if(bin_index==NUM_BINS)bin_index--;// 处理边界情况bins[bin_index]++;}std::cout<<"\nLatency distribution:\n";for(inti=0;i<NUM_BINS;i++){doublebin_start=min_val+i*bin_width;doublebin_end=bin_start+bin_width;std::cout<<std::fixed<<std::setprecision(1)<<" "<<std::setw(6)<<bin_start<<" - "<<std::setw(6)<<bin_end<<" ms: "<<std::string(bins[i]*50/data.size(),'#')<<" ("<<bins[i]<<")\n";}}voidRunBandwidthTest(intduration_sec=10,intpacket_size=1400){std::cout<<"\n=== Bandwidth Test ===\n";std::cout<<"Duration: "<<duration_sec<<" seconds\n";std::cout<<"Packet size: "<<packet_size<<" bytes\n\n";UdpClientclient(server_ip_,server_port_);if(!client.Init()){std::cerr<<"Failed to initialize client"<<std::endl;return;}std::vector<char>packet_data(packet_size,'B');autostart_time=std::chrono::steady_clock::now();autoend_time=start_time+std::chrono::seconds(duration_sec);uint64_ttotal_packets=0;uint64_ttotal_bytes=0;uint64_tsuccessful_responses=0;std::cout<<"Testing bandwidth...\n";while(std::chrono::steady_clock::now()<end_time){// 发送数据包if(client.Send(packet_data.data(),packet_size)){total_packets++;total_bytes+=packet_size;}// 尝试接收响应(非阻塞)std::string response;if(client.Receive(response,10)){// 10ms超时successful_responses++;}// 控制发送速率(约1000 packets/second)std::this_thread::sleep_for(std::chrono::microseconds(900));}autoactual_end=std::chrono::steady_clock::now();autoactual_duration=std::chrono::duration_cast<std::chrono::milliseconds>(actual_end-start_time);// 输出结果std::cout<<"\n=== Bandwidth Test Results ===\n";std::cout<<"Actual duration: "<<actual_duration.count()<<" ms\n";std::cout<<"Packets sent: "<<total_packets<<"\n";std::cout<<"Total data sent: "<<(total_bytes/1024.0/1024.0)<<" MB\n";std::cout<<"Responses received: "<<successful_responses<<"\n";if(actual_duration.count()>0){doublepackets_per_sec=total_packets*1000.0/actual_duration.count();doublebandwidth_mbps=(total_bytes*8.0)/(actual_duration.count()*1000.0);std::cout<<"Send rate: "<<packets_per_sec<<" packets/second\n";std::cout<<"Bandwidth: "<<bandwidth_mbps<<" Mbps\n";std::cout<<"Response rate: "<<(successful_responses*1000.0/actual_duration.count())<<" packets/second\n";}doubleloss_rate=0;if(total_packets>0){loss_rate=(1.0-(successful_responses/(double)total_packets))*100.0;std::cout<<"Estimated loss rate: "<<std::fixed<<std::setprecision(2)<<loss_rate<<"%\n";}}};intmain(intargc,char*argv[]){std::string server_ip="127.0.0.1";intserver_port=8080;std::string test_type="latency";// 解析命令行参数for(inti=1;i<argc;i++){std::string arg=argv[i];if(arg=="-s"||arg=="--server"){if(i+1<argc)server_ip=argv[++i];}elseif(arg=="-p"||arg=="--port"){if(i+1<argc)server_port=std::atoi(argv[++i]);}elseif(arg=="-t"||arg=="--test"){if(i+1<argc)test_type=argv[++i];}elseif(arg=="-h"||arg=="--help"){std::cout<<"Network Test Tool\n\n";std::cout<<"Usage: "<<argv[0]<<" [options]\n\n";std::cout<<"Options:\n";std::cout<<" -s, --server IP Server IP address\n";std::cout<<" -p, --port PORT Server port\n";std::cout<<" -t, --test TYPE Test type (latency|bandwidth)\n";std::cout<<" -h, --help Show this help\n";return0;}}NetworkTestTooltester(server_ip,server_port);if(test_type=="latency"){tester.RunLatencyTest();}elseif(test_type=="bandwidth"){tester.RunBandwidthTest();}else{std::cerr<<"Unknown test type: "<<test_type<<std::endl;std::cerr<<"Available types: latency, bandwidth"<<std::endl;return1;}return0;}

五、源代码

5.1 Log.hpp - 日志系统

#ifndefLOG_HPP#defineLOG_HPP#include<iostream>#include<fstream>#include<string>#include<sstream>#include<iomanip>#include<ctime>#include<mutex>#include<memory>// 日志级别enumclassLogLevel{DEBUG,INFO,WARN,ERROR,FATAL};classLogger{private:staticstd::shared_ptr<Logger>instance_;std::ofstream log_file_;LogLevel min_level_;std::mutex log_mutex_;boolconsole_output_;// 私有构造函数Logger():min_level_(LogLevel::INFO),console_output_(true){}public:// 删除拷贝构造函数和赋值运算符Logger(constLogger&)=delete;Logger&operator=(constLogger&)=delete;// 获取单例实例staticLogger&Instance(){staticstd::shared_ptr<Logger>instance(newLogger());return*instance;}// 初始化日志系统boolInit(conststd::string&filename="",LogLevel min_level=LogLevel::INFO,boolconsole=true){std::lock_guard<std::mutex>lock(log_mutex_);min_level_=min_level;console_output_=console;if(!filename.empty()){log_file_.open(filename,std::ios::app);if(!log_file_.is_open()){std::cerr<<"Failed to open log file: "<<filename<<std::endl;returnfalse;}}returntrue;}// 设置日志级别voidSetLevel(LogLevel level){std::lock_guard<std::mutex>lock(log_mutex_);min_level_=level;}// 启用/禁用控制台输出voidEnableConsole(boolenable){std::lock_guard<std::mutex>lock(log_mutex_);console_output_=enable;}// 记录日志voidLog(LogLevel level,conststd::string&message,constchar*file=nullptr,intline=0){if(level<min_level_){return;}std::lock_guard<std::mutex>lock(log_mutex_);// 获取当前时间autonow=std::chrono::system_clock::now();autonow_time=std::chrono::system_clock::to_time_t(now);autonow_ms=std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch())%1000;// 格式化时间std::tm*tm_info=std::localtime(&now_time);chartime_buffer[80];std::strftime(time_buffer,sizeof(time_buffer),"%Y-%m-%d %H:%M:%S",tm_info);// 日志级别字符串constchar*level_str="";switch(level){caseLogLevel::DEBUG:level_str="DEBUG";break;caseLogLevel::INFO:level_str="INFO";break;caseLogLevel::WARN:level_str="WARN";break;caseLogLevel::ERROR:level_str="ERROR";break;caseLogLevel::FATAL:level_str="FATAL";break;}// 构建日志消息std::stringstream ss;ss<<"["<<time_buffer<<"."<<std::setfill('0')<<std::setw(3)<<now_ms.count()<<"] "<<"["<<level_str<<"] ";if(file!=nullptr){ss<<"["<<file<<":"<<line<<"] ";}ss<<message;std::string log_message=ss.str();// 输出到控制台if(console_output_){std::ostream&stream=(level>=LogLevel::WARN)?std::cerr:std::cout;stream<<log_message<<std::endl;if(level==LogLevel::FATAL){stream<<"Fatal error, terminating..."<<std::endl;}}// 输出到文件if(log_file_.is_open()){log_file_<<log_message<<std::endl;log_file_.flush();if(level==LogLevel::FATAL){log_file_<<"Fatal error, terminating..."<<std::endl;log_file_.flush();}}// 如果是致命错误,终止程序if(level==LogLevel::FATAL){std::exit(EXIT_FAILURE);}}// 关闭日志voidClose(){std::lock_guard<std::mutex>lock(log_mutex_);if(log_file_.is_open()){log_file_.close();}}~Logger(){Close();}};// 日志宏#defineLOG_DEBUG(msg)Logger::Instance().Log(LogLevel::DEBUG,msg,__FILE__,__LINE__)#defineLOG_INFO(msg)Logger::Instance().Log(LogLevel::INFO,msg,__FILE__,__LINE__)#defineLOG_WARN(msg)Logger::Instance().Log(LogLevel::WARN,msg,__FILE__,__LINE__)#defineLOG_ERROR(msg)Logger::Instance().Log(LogLevel::ERROR,msg,__FILE__,__LINE__)#defineLOG_FATAL(msg)Logger::Instance().Log(LogLevel::FATAL,msg,__FILE__,__LINE__)#endif// LOG_HPP

5.2 Makefile - 构建系统

# Makefile for UDP Network System # Compiler and flags CXX = g++ CXXFLAGS = -std=c++11 -Wall -Wextra -O2 -pthread DEBUG_FLAGS = -g -DDEBUG RELEASE_FLAGS = -O3 -DNDEBUG # Directories SRC_DIR = src OBJ_DIR = obj BIN_DIR = bin INC_DIR = include # Source files SERVER_SRCS = $(SRC_DIR)/UdpServer.cpp $(SRC_DIR)/Main.cpp $(SRC_DIR)/Log.cpp CLIENT_SRCS = $(SRC_DIR)/UdpClient.cpp TEST_SRCS = $(SRC_DIR)/TestUdpServer.cpp INTEGRATION_SRCS = $(SRC_DIR)/IntegrationTest.cpp NETTEST_SRCS = $(SRC_DIR)/NetworkTestTool.cpp # Object files SERVER_OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(SERVER_SRCS)) CLIENT_OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(CLIENT_SRCS)) TEST_OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(TEST_SRCS)) INTEGRATION_OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(INTEGRATION_SRCS)) NETTEST_OBJS = $(patsubst $(SRC_DIR)/%.cpp,$(OBJ_DIR)/%.o,$(NETTEST_SRCS)) # Executables SERVER_EXE = $(BIN_DIR)/udp_server CLIENT_EXE = $(BIN_DIR)/udp_client TEST_EXE = $(BIN_DIR)/test_server INTEGRATION_EXE = $(BIN_DIR)/integration_test NETTEST_EXE = $(BIN_DIR)/network_test # Include paths INCLUDES = -I$(INC_DIR) # Libraries LIBS = -lpthread TEST_LIBS = $(LIBS) -lgtest -lgtest_main # Default target all: directories server client # Create directories directories: @mkdir -p $(OBJ_DIR) $(BIN_DIR) # Server build server: $(SERVER_EXE) $(SERVER_EXE): $(SERVER_OBJS) $(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) # Client build client: $(CLIENT_EXE) $(CLIENT_EXE): $(CLIENT_OBJS) $(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) # Test build test: $(TEST_EXE) $(TEST_EXE): $(TEST_OBJS) $(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $@ $(TEST_LIBS) # Integration test build integration: $(INTEGRATION_EXE) $(INTEGRATION_EXE): $(INTEGRATION_OBJS) $(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) # Network test tool build nettest: $(NETTEST_EXE) $(NETTEST_EXE): $(NETTEST_OBJS) $(CXX) $(CXXFLAGS) $(INCLUDES) $^ -o $@ $(LIBS) # Compile source files $(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ # Debug build debug: CXXFLAGS += $(DEBUG_FLAGS) debug: all # Release build release: CXXFLAGS += $(RELEASE_FLAGS) release: all # Static analysis with cppcheck check: cppcheck --enable=all --suppress=missingIncludeSystem $(SRC_DIR) $(INC_DIR) # Run tests run-test: test $(TEST_EXE) run-integration: integration $(INTEGRATION_EXE) # Clean build files clean: rm -rf $(OBJ_DIR) $(BIN_DIR) rm -f *.log # Install system-wide (requires root) install: release cp $(SERVER_EXE) /usr/local/bin/udp_server cp $(CLIENT_EXE) /usr/local/bin/udp_client chmod +x /usr/local/bin/udp_server /usr/local/bin/udp_client # Uninstall uninstall: rm -f /usr/local/bin/udp_server /usr/local/bin/udp_client # Run server run-server: server $(SERVER_EXE) -p 8080 # Run client run-client: client $(CLIENT_EXE) -s 127.0.0.1 -p 8080 # Run network test run-nettest: nettest $(NETTEST_EXE) -s 127.0.0.1 -p 8080 -t latency # Generate documentation doc: doxygen Doxyfile # Help help: @echo "Available targets:" @echo " all - Build server and client (default)" @echo " server - Build server only" @echo " client - Build client only" @echo " test - Build and run unit tests" @echo " integration - Build integration tests" @echo " nettest - Build network test tool" @echo " debug - Build with debug flags" @echo " release - Build with release flags" @echo " check - Run static analysis" @echo " run-test - Run unit tests" @echo " run-integration - Run integration tests" @echo " clean - Remove build files" @echo " install - Install system-wide" @echo " uninstall - Uninstall" @echo " run-server - Run server on port 8080" @echo " run-client - Run client connecting to localhost:8080" @echo " run-nettest - Run network latency test" @echo " doc - Generate documentation" @echo " help - Show this help" .PHONY: all directories server client test integration nettest debug release \ check run-test run-integration clean install uninstall run-server \ run-client run-nettest doc help

5.3 完整的UdpServer.hpp

#ifndefUDPSERVER_HPP#defineUDPSERVER_HPP#include<iostream>#include<string>#include<cstring>#include<cstdlib>#include<unistd.h>#include<arpa/inet.h>#include<sys/socket.h>#include<sys/types.h>#include<netinet/in.h>#include<thread>#include<vector>#include<memory>#include<atomic>#include<functional>#include<queue>#include<mutex>#include<condition_variable>#include"Log.hpp"classUdpServer{protected:intport_;// 服务器端口intsockfd_;// 套接字描述符std::atomic<bool>is_running_;// 服务器运行状态structsockaddr_inserver_addr_;// 服务器地址结构structsockaddr_inclient_addr_;// 客户端地址结构socklen_t client_addr_len_;// 客户端地址长度// 服务器配置参数size_t buffer_size_;// 缓冲区大小inttimeout_sec_;// 接收超时时间(秒)inttimeout_usec_;// 接收超时时间(微秒)boolreuse_addr_;// 是否重用地址public:// 构造函数explicitUdpServer(intport=8080);// 析构函数virtual~UdpServer();// 禁止拷贝构造和赋值UdpServer(constUdpServer&)=delete;UdpServer&operator=(constUdpServer&)=delete;// 初始化服务器virtualboolInit();// 运行服务器virtualvoidRun();// 停止服务器virtualvoidStop();// 设置配置参数voidSetBufferSize(size_t size){buffer_size_=size;}voidSetTimeout(intsec,intusec=0){timeout_sec_=sec;timeout_usec_=usec;}voidSetReuseAddr(boolreuse){reuse_addr_=reuse;}// 获取服务器信息intGetPort()const{returnport_;}boolIsRunning()const{returnis_running_;}protected:// 创建套接字virtualboolCreateSocket();// 绑定地址virtualboolBindAddress();// 设置套接字选项virtualboolSetSocketOptions();// 处理接收到的数据virtualvoidProcessData(constchar*data,ssize_t len,conststructsockaddr_in&client_addr);// 发送响应virtualboolSendResponse(constchar*data,ssize_t len,conststructsockaddr_in&client_addr);// 清理资源virtualvoidCleanup();};// 高级UDP服务器(带线程池)classAdvancedUdpServer:publicUdpServer{private:std::vector<std::thread>worker_threads_;std::atomic<int>thread_count_;intmax_workers_;// 线程池和工作队列structTask{std::vector<char>data;structsockaddr_inclient_addr;time_t receive_time;Task(conststd::vector<char>&d,conststructsockaddr_in&addr):data(d),client_addr(addr),receive_time(time(nullptr)){}};std::queue<Task>task_queue_;std::mutex queue_mutex_;std::condition_variable queue_cv_;std::atomic<bool>workers_running_;public:AdvancedUdpServer(intport=8080,intmax_workers=4);~AdvancedUdpServer()override;boolInit()override;voidRun()override;voidStop()override;// 获取线程池状态intGetActiveWorkers()const{returnthread_count_;}size_tGetQueueSize()const{returntask_queue_.size();}private:voidWorkerThread(intthread_id);voidProcessTask(constTask&task,intthread_id);// 重写基类方法boolSetSocketOptions()override;voidProcessData(constchar*data,ssize_t len,conststructsockaddr_in&client_addr)override;// 任务调度voidAddTask(conststd::vector<char>&data,conststructsockaddr_in&client_addr);};#endif// UDPSERVER_HPP

5.4 完整的Main.cpp

#include<iostream>#include<csignal>#include<cstdlib>#include<memory>#include<getopt.h>#include"UdpServer.hpp"#include"AdvancedUdpServer.hpp"// 全局服务器指针,用于信号处理std::unique_ptr<UdpServer>g_server;// 信号处理函数voidSignalHandler(intsignal){std::cout<<"\nReceived signal "<<signal<<", shutting down..."<<std::endl;if(g_server){g_server->Stop();}}// 显示使用帮助voidShowUsage(constchar*program_name){std::cout<<"UDP Server v1.0 - High Performance UDP Server Implementation\n";std::cout<<"Build Date: "<<__DATE__<<" "<<__TIME__<<"\n\n";std::cout<<"Usage: "<<program_name<<" [options]\n\n";std::cout<<"Options:\n";std::cout<<" -p, --port PORT Server port (default: 8080)\n";std::cout<<" -b, --buffer SIZE Buffer size in bytes (default: 4096)\n";std::cout<<" -t, --timeout SEC Receive timeout in seconds (default: 5)\n";std::cout<<" -w, --workers NUM Number of worker threads (default: 1)\n";std::cout<<" -a, --advanced Use advanced server with thread pool\n";std::cout<<" -r, --no-reuse Disable address reuse\n";std::cout<<" -v, --verbose Enable verbose logging\n";std::cout<<" -d, --daemon Run as daemon\n";std::cout<<" -c, --config FILE Load configuration from file\n";std::cout<<" -h, --help Show this help message\n";std::cout<<"\nExamples:\n";std::cout<<" "<<program_name<<" -p 9000 -b 8192\n";std::cout<<" "<<program_name<<" --port 8080 --workers 4 --advanced\n";std::cout<<" "<<program_name<<" --daemon --config /etc/udp-server.conf\n";}// 服务器配置结构structServerConfig{intport=8080;size_t buffer_size=4096;inttimeout_sec=5;inttimeout_usec=0;intworkers=1;booladvanced=false;boolreuse_addr=true;booldaemon=false;boolverbose=false;std::string config_file;std::string log_file="udp_server.log";LogLevel log_level=LogLevel::INFO;};// 解析命令行参数ServerConfigParseArguments(intargc,char*argv[]){ServerConfig config;structoptionlong_options[]={{"port",required_argument,0,'p'},{"buffer",required_argument,0,'b'},{"timeout",required_argument,0,'t'},{"workers",required_argument,0,'w'},{"advanced",no_argument,0,'a'},{"no-reuse",no_argument,0,'r'},{"verbose",no_argument,0,'v'},{"daemon",no_argument,0,'d'},{"config",required_argument,0,'c'},{"help",no_argument,0,'h'},{0,0,0,0}};intopt;intoption_index=0;while((opt=getopt_long(argc,argv,"p:b:t:w:arvdc:h",long_options,&option_index))!=-1){switch(opt){case'p':config.port=std::atoi(optarg);if(config.port<=0||config.port>65535){std::cerr<<"Error: Port must be between 1 and 65535"<<std::endl;exit(EXIT_FAILURE);}break;case'b':config.buffer_size=std::atoi(optarg);if(config.buffer_size<1024||config.buffer_size>65536){std::cerr<<"Error: Buffer size must be between 1024 and 65536"<<std::endl;exit(EXIT_FAILURE);}break;case't':config.timeout_sec=std::atoi(optarg);if(config.timeout_sec<0){std::cerr<<"Error: Timeout must be non-negative"<<std::endl;exit(EXIT_FAILURE);}break;case'w':config.workers=std::atoi(optarg);if(config.workers<1||config.workers>32){std::cerr<<"Error: Number of workers must be between 1 and 32"<<std::endl;exit(EXIT_FAILURE);}break;case'a':config.advanced=true;break;case'r':config.reuse_addr=false;break;case'v':config.verbose=true;config.log_level=LogLevel::DEBUG;break;case'd':config.daemon=true;break;case'c':config.config_file=optarg;// 这里可以添加从配置文件加载配置的逻辑break;case'h':ShowUsage(argv[0]);exit(EXIT_SUCCESS);default:std::cerr<<"Error: Unknown option"<<std::endl;ShowUsage(argv[0]);exit(EXIT_FAILURE);}}returnconfig;}// 守护进程化voidDaemonize(){pid_t pid=fork();if(pid<0){std::cerr<<"Failed to fork daemon: "<<strerror(errno)<<std::endl;exit(EXIT_FAILURE);}if(pid>0){// 父进程退出exit(EXIT_SUCCESS);}// 子进程继续umask(0);pid_t sid=setsid();if(sid<0){std::cerr<<"Failed to create new session: "<<strerror(errno)<<std::endl;exit(EXIT_FAILURE);}if((chdir("/"))<0){std::cerr<<"Failed to change directory: "<<strerror(errno)<<std::endl;exit(EXIT_FAILURE);}// 关闭标准文件描述符close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 重定向到/dev/nullopen("/dev/null",O_RDONLY);open("/dev/null",O_WRONLY);open("/dev/null",O_RDWR);}intmain(intargc,char*argv[]){// 解析命令行参数ServerConfig config=ParseArguments(argc,argv);// 如果需要,转换为守护进程if(config.daemon){Daemonize();}// 注册信号处理signal(SIGINT,SignalHandler);signal(SIGTERM,SignalHandler);signal(SIGPIPE,SIG_IGN);// 忽略管道破裂信号try{if(!config.daemon){std::cout<<"=== UDP Server Starting ===\n";std::cout<<"Version: 1.0\n";std::cout<<"Port: "<<config.port<<"\n";std::cout<<"Buffer size: "<<config.buffer_size<<" bytes\n";std::cout<<"Timeout: "<<config.timeout_sec<<" seconds\n";std::cout<<"Workers: "<<config.workers<<"\n";std::cout<<"Mode: "<<(config.advanced?"Advanced (Thread Pool)":"Basic")<<"\n";std::cout<<"Log file: "<<config.log_file<<"\n";std::cout<<"Log level: "<<(config.verbose?"DEBUG":"INFO")<<"\n";std::cout<<"===========================\n\n";}// 初始化日志系统Logger::Instance().Init(config.log_file,config.log_level,!config.daemon);LOG_INFO("UDP Server starting...");LOG_INFO("Configuration: port=%d, buffer=%zu, timeout=%d, workers=%d, mode=%s",config.port,config.buffer_size,config.timeout_sec,config.workers,config.advanced?"advanced":"basic");// 创建服务器实例if(config.advanced){g_server=std::make_unique<AdvancedUdpServer>(config.port,config.workers);}else{g_server=std::make_unique<UdpServer>(config.port);}// 配置服务器g_server->SetBufferSize(config.buffer_size);g_server->SetTimeout(config.timeout_sec,config.timeout_usec);g_server->SetReuseAddr(config.reuse_addr);// 初始化服务器if(!g_server->Init()){LOG_FATAL("Failed to initialize server");returnEXIT_FAILURE;}LOG_INFO("Server initialized successfully");if(!config.daemon){std::cout<<"Server initialized successfully\n";std::cout<<"Press Ctrl+C to stop the server\n\n";}// 运行服务器g_server->Run();}catch(conststd::exception&e){LOG_ERROR("Exception: %s",e.what());if(!config.daemon){std::cerr<<"Exception: "<<e.what()<<std::endl;}returnEXIT_FAILURE;}catch(...){LOG_ERROR("Unknown exception occurred");if(!config.daemon){std::cerr<<"Unknown exception occurred"<<std::endl;}returnEXIT_FAILURE;}LOG_INFO("Server stopped gracefully");if(!config.daemon){std::cout<<"\nServer stopped gracefully"<<std::endl;}returnEXIT_SUCCESS;}

总结

通过本文的详细讲解和代码实现,我们完成了一个完整的UDP网络通信系统的设计与实现。这个系统具有以下特点和优势:

1. 系统架构特点

模块化设计

  • 服务器和客户端分离,职责明确
  • 日志系统独立,便于维护和扩展
  • 配置系统灵活,支持命令行和配置文件

高性能设计

  • 支持多线程处理,充分利用多核CPU
  • 智能缓冲区管理,避免内存碎片
  • 异步I/O操作,减少等待时间

可靠性保障

  • 完善的错误处理和异常恢复机制
  • 连接状态监控和自动清理
  • 详细的日志记录,便于问题排查

2. 关键技术点

套接字编程核心

  • 深入理解了socket()bind()recvfrom()sendto()等系统调用
  • 掌握了地址转换函数如inet_pton()inet_ntop()的使用
  • 理解了字节序转换的重要性

并发处理

  • 多线程编程的最佳实践
  • 线程安全的队列实现
  • 条件变量的正确使用

网络优化

  • 缓冲区大小的优化配置
  • 超时机制的合理设置
  • 数据包分片和重组处理

3. 实际应用价值

教育意义

  • 完整的网络编程教学示例
  • 良好的编码规范和架构设计示范
  • 详细的注释和文档说明

实用价值

  • 可直接用于实际项目的网络通信模块
  • 提供了性能测试和监控工具
  • 支持多种运行模式和配置选项

扩展性

  • 易于添加新的协议支持
  • 支持插件式功能扩展
  • 良好的接口设计,便于二次开发

4. 性能优化建议

服务器端优化

  1. 使用epoll或kqueue等I/O多路复用技术处理更多并发连接
  2. 实现连接池减少连接建立开销
  3. 使用内存池技术减少内存分配开销

客户端优化

  1. 实现请求合并,减少网络包数量
  2. 添加压缩支持,减少数据传输量
  3. 实现智能重传机制,提高可靠性

网络优化

  1. 支持IPv6双栈
  2. 添加QUIC协议支持
  3. 实现流量控制和拥塞避免算法

5. 安全考虑

基础安全

  • 输入验证和边界检查
  • 缓冲区溢出防护
  • 资源限制和配额管理

高级安全

  • 支持TLS/DTLS加密传输
  • 实现身份验证和授权机制
  • 添加DoS攻击防护

6. 未来发展方向

功能增强

  • 添加Web管理界面
  • 支持集群部署
  • 实现负载均衡

性能提升

  • 支持RDMA高速网络
  • 添加GPU加速支持
  • 实现零拷贝技术

生态系统

  • 提供多种语言SDK
  • 支持云原生部署
  • 集成监控告警系统

通过本系统的实现,读者不仅能够掌握UDP网络编程的核心技术,还能够学习到软件工程中的良好实践,包括模块化设计、错误处理、性能优化、测试策略等。这个系统可以作为学习网络编程的绝佳范例,也可以作为实际项目的基础框架进行扩展和优化。

网络编程是一个既深又广的领域,本文只是抛砖引玉。希望读者能够在此基础上继续探索,深入研究网络协议的各个层面,从应用层到底层实现,不断积累经验,最终成为网络编程的专家。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/20 6:39:59

Solidity函数修改器中包含return的执行顺序

关键概念 在 Solidity 中,如果在修改器中执行 return,会立即终止当前函数的执行,不会执行目标函数体,但会执行修改器中 _; 之后的代码。 执行顺序详解 基本执行流程 // SPDX-License-Identifier: MIT pragma solidity ^0.8.0;contract ModifierReturn {uint256 public …

作者头像 李华
网站建设 2026/6/15 13:06:39

Node.js C++ Addons:FFI 与 N-API 的性能与兼容性对比

欢迎来到本次关于Node.js C Addons的深入探讨。在Node.js生态系统中&#xff0c;JavaScript以其单线程、事件驱动的非阻塞I/O模型而闻名&#xff0c;非常适合处理高并发的网络应用。然而&#xff0c;当面临计算密集型任务&#xff08;如图像处理、密码学、科学计算&#xff09;…

作者头像 李华
网站建设 2026/6/20 21:30:45

小学生学C++编程 (变量精讲)

一、&#x1f389;✨《C 变量王国大冒险》✨&#x1f389;1、&#x1f3f0; 什么是变量&#xff1f;——“贴着名字的小盒子”想象你来到一个神奇的王国——变量王国&#xff01; 这里有很多很多小盒子&#xff0c;每个盒子上都贴着一个名字&#xff0c;比如&#xff1a;age&am…

作者头像 李华
网站建设 2026/6/16 2:21:30

MYSQL-存储引擎

存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的&#xff0c;而不是基于库的&#xff0c;所以存储引擎也被称为表类型。MySQL的存储引擎是数据库的底层核心组件&#xff0c;它决定了数据如何存储、如何索引、是否支持事务以及如何实现并发…

作者头像 李华
网站建设 2026/6/15 18:07:55

132页RAG实践手册:构建知识库和问答系统的实战指南

在人工智能技术日新月异的今天&#xff0c;越来越多的人希望能够拥有属于自己的智能助手&#xff0c;提升工作效率、优化知识管理&#xff0c;甚至打造个人品牌。《RAG Handbook》正是为此而生。 本书将系统性地介绍如何从零开始&#xff0c;基于RAG&#xff08;Retrieval‑Aug…

作者头像 李华