
1. 项目背景与核心目标在Linux网络编程中TCP和UDP是两种最基础的传输层协议。TCP提供可靠的、面向连接的通信而UDP则是无连接的、尽最大努力交付的协议。通过实现一个简单的计算器服务我们可以深入理解这两种协议在实际应用中的差异和适用场景。这个项目的主要目标是实现基于TCP协议的计算器服务端和客户端实现基于UDP协议的计算器服务端和客户端对比两种协议在实现相同功能时的差异掌握Linux下socket编程的基本方法2. TCP服务端实现详解2.1 TCP服务端核心流程TCP服务端的基本工作流程如下创建socket使用socket()系统调用创建一个TCP socket绑定地址使用bind()将socket绑定到特定的IP地址和端口监听连接使用listen()开始监听连接请求接受连接使用accept()接受客户端的连接数据交换使用read()/write()或send()/recv()进行数据收发关闭连接使用close()关闭连接2.2 关键代码实现#include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/types.h #include sys/socket.h #include netinet/in.h #define PORT 8080 int main() { int sockfd, newsockfd; socklen_t clilen; char buffer[256]; struct sockaddr_in serv_addr, cli_addr; // 1. 创建socket sockfd socket(AF_INET, SOCK_STREAM, 0); if (sockfd 0) { perror(ERROR opening socket); exit(1); } // 2. 绑定地址 bzero((char *) serv_addr, sizeof(serv_addr)); serv_addr.sin_family AF_INET; serv_addr.sin_addr.s_addr INADDR_ANY; serv_addr.sin_port htons(PORT); if (bind(sockfd, (struct sockaddr *) serv_addr, sizeof(serv_addr)) 0) { perror(ERROR on binding); exit(1); } // 3. 监听连接 listen(sockfd, 5); clilen sizeof(cli_addr); while (1) { // 4. 接受连接 newsockfd accept(sockfd, (struct sockaddr *) cli_addr, clilen); if (newsockfd 0) { perror(ERROR on accept); continue; } // 5. 数据交换 bzero(buffer, 256); int n read(newsockfd, buffer, 255); if (n 0) { perror(ERROR reading from socket); close(newsockfd); continue; } // 处理计算请求 int num1, num2; char op; sscanf(buffer, %d %c %d, num1, op, num2); int result; switch(op) { case : result num1 num2; break; case -: result num1 - num2; break; case *: result num1 * num2; break; case /: result num1 / num2; break; default: result 0; } sprintf(buffer, %d, result); n write(newsockfd, buffer, strlen(buffer)); if (n 0) { perror(ERROR writing to socket); } // 6. 关闭连接 close(newsockfd); } close(sockfd); return 0; }2.3 TCP实现注意事项连接管理TCP是面向连接的协议每个客户端连接都需要独立的socket处理错误处理每个系统调用都可能失败必须检查返回值并适当处理错误字节序转换网络字节序是大端序需要使用htons/htonl等函数进行转换并发处理上述代码是单线程的实际应用中可能需要多线程或IO多路复用3. UDP服务端实现详解3.1 UDP服务端核心流程UDP服务端的工作流程比TCP简单创建socket使用socket()系统调用创建一个UDP socket绑定地址使用bind()将socket绑定到特定的IP地址和端口接收数据使用recvfrom()接收客户端数据发送响应使用sendto()发送响应数据重复步骤3-4处理后续请求3.2 关键代码实现#include stdio.h #include stdlib.h #include string.h #include unistd.h #include sys/types.h #include sys/socket.h #include netinet/in.h #define PORT 8080 int main() { int sockfd; socklen_t clilen; char buffer[256]; struct sockaddr_in serv_addr, cli_addr; // 1. 创建socket sockfd socket(AF_INET, SOCK_DGRAM, 0); if (sockfd 0) { perror(ERROR opening socket); exit(1); } // 2. 绑定地址 bzero((char *) serv_addr, sizeof(serv_addr)); serv_addr.sin_family AF_INET; serv_addr.sin_addr.s_addr INADDR_ANY; serv_addr.sin_port htons(PORT); if (bind(sockfd, (struct sockaddr *) serv_addr, sizeof(serv_addr)) 0) { perror(ERROR on binding); exit(1); } while (1) { // 3. 接收数据 clilen sizeof(cli_addr); bzero(buffer, 256); int n recvfrom(sockfd, buffer, 255, 0, (struct sockaddr *) cli_addr, clilen); if (n 0) { perror(ERROR in recvfrom); continue; } // 处理计算请求 int num1, num2; char op; sscanf(buffer, %d %c %d, num1, op, num2); int result; switch(op) { case : result num1 num2; break; case -: result num1 - num2; break; case *: result num1 * num2; break; case /: result num1 / num2; break; default: result 0; } sprintf(buffer, %d, result); // 4. 发送响应 n sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *) cli_addr, clilen); if (n 0) { perror(ERROR in sendto); } } close(sockfd); return 0; }3.3 UDP实现注意事项无连接特性UDP不需要建立连接每个数据包都是独立的数据包边界UDP保留数据包边界recvfrom()一次读取一个完整的数据包可靠性问题UDP不保证数据可靠传输应用层需要处理丢包和乱序问题最大报文长度UDP单次传输的数据量不应超过MTU(通常1500字节)4. 客户端实现与协议对比4.1 TCP客户端实现#include stdio.h #include stdlib.h #include unistd.h #include string.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include netdb.h #define PORT 8080 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in serv_addr; struct hostent *server; char buffer[256]; if (argc 3) { fprintf(stderr,usage %s hostname operation\n, argv[0]); exit(0); } // 创建socket sockfd socket(AF_INET, SOCK_STREAM, 0); if (sockfd 0) { perror(ERROR opening socket); exit(1); } server gethostbyname(argv[1]); if (server NULL) { fprintf(stderr,ERROR, no such host\n); exit(0); } bzero((char *) serv_addr, sizeof(serv_addr)); serv_addr.sin_family AF_INET; bcopy((char *)server-h_addr, (char *)serv_addr.sin_addr.s_addr, server-h_length); serv_addr.sin_port htons(PORT); // 建立连接 if (connect(sockfd, (struct sockaddr *) serv_addr, sizeof(serv_addr)) 0) { perror(ERROR connecting); exit(1); } // 发送请求 printf(Please enter the operation (e.g. 1 2): ); bzero(buffer, 256); fgets(buffer, 255, stdin); int n write(sockfd, buffer, strlen(buffer)); if (n 0) { perror(ERROR writing to socket); exit(1); } // 接收响应 bzero(buffer, 256); n read(sockfd, buffer, 255); if (n 0) { perror(ERROR reading from socket); exit(1); } printf(Result: %s\n, buffer); close(sockfd); return 0; }4.2 UDP客户端实现#include stdio.h #include stdlib.h #include unistd.h #include string.h #include sys/types.h #include sys/socket.h #include netinet/in.h #include netdb.h #define PORT 8080 int main(int argc, char *argv[]) { int sockfd; struct sockaddr_in serv_addr; struct hostent *server; char buffer[256]; socklen_t servlen sizeof(serv_addr); if (argc 3) { fprintf(stderr,usage %s hostname operation\n, argv[0]); exit(0); } // 创建socket sockfd socket(AF_INET, SOCK_DGRAM, 0); if (sockfd 0) { perror(ERROR opening socket); exit(1); } server gethostbyname(argv[1]); if (server NULL) { fprintf(stderr,ERROR, no such host\n); exit(0); } bzero((char *) serv_addr, sizeof(serv_addr)); serv_addr.sin_family AF_INET; bcopy((char *)server-h_addr, (char *)serv_addr.sin_addr.s_addr, server-h_length); serv_addr.sin_port htons(PORT); // 发送请求 printf(Please enter the operation (e.g. 1 2): ); bzero(buffer, 256); fgets(buffer, 255, stdin); int n sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *) serv_addr, servlen); if (n 0) { perror(ERROR in sendto); exit(1); } // 接收响应 bzero(buffer, 256); n recvfrom(sockfd, buffer, 255, 0, (struct sockaddr *) serv_addr, servlen); if (n 0) { perror(ERROR in recvfrom); exit(1); } printf(Result: %s\n, buffer); close(sockfd); return 0; }4.3 TCP与UDP协议对比特性TCPUDP连接方式面向连接无连接可靠性可靠传输有确认机制不可靠传输无确认顺序保证保证数据顺序不保证顺序流量控制有滑动窗口机制无流量控制拥塞控制有拥塞控制算法无拥塞控制头部大小较大(20字节)较小(8字节)传输效率较低较高适用场景文件传输、网页浏览等需要可靠传输的场景视频流、DNS查询等实时性要求高的场景5. 项目扩展与优化建议5.1 并发处理优化当前的TCP服务端实现是单线程的只能同时处理一个客户端连接。可以通过以下方式改进多线程为每个客户端连接创建一个新线程多进程使用fork()为每个客户端创建子进程IO多路复用使用select/poll/epoll实现单线程处理多个连接5.2 协议设计优化当前实现使用简单的空格分隔格式传输计算表达式可以考虑使用JSON等结构化数据格式添加协议版本号增加错误码和错误信息返回5.3 安全性考虑添加输入验证防止缓冲区溢出实现简单的认证机制考虑使用TLS加密通信5.4 性能测试与调优使用工具如iperf测试吞吐量调整socket缓冲区大小测试不同并发量下的性能表现在实际项目中选择TCP还是UDP取决于具体需求。如果需要可靠传输TCP是更好的选择如果对实时性要求高且可以容忍少量丢包UDP可能更合适。通过这个简单的计算器项目我们可以清晰地看到两种协议在实际应用中的差异和各自的适用场景。