UDP套接字通信异常:客户端无法接收服务器回复的问题排查
UDP通信客户端无法接收服务器回复的问题
在UDP套接字通信场景中,客户端发送消息后,服务器能成功接收并输出该消息,随后向客户端发送回复,但客户端无法接收到该回复。以下是客户端与服务器的UDP套接字实现代码:
客户端UDP套接字代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 30 void error_handling(char *message); int main(int argc, char *argv[]) { int sock; char message[BUF_SIZE]; char clntmessage[BUF_SIZE]; char servmessage[BUF_SIZE]; int str_len; socklen_t adr_sz; struct sockaddr_in serv_adr, from_adr; if(argc!=3){ printf("Usage : %s <IP> <port>\n", argv[0]); exit(1); } sock=socket(PF_INET, SOCK_DGRAM, 0); if(sock==-1) error_handling("socket() error"); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=inet_addr(argv[1]); serv_adr.sin_port=htons(atoi(argv[2])); while(1) { fputs("Insert message(q or Q quit): ", stdout); fgets(clntmessage, sizeof(message), stdin); sendto(sock, clntmessage, strlen(clntmessage), 0, (struct sockaddr*)&serv_adr, sizeof(serv_adr)); if (!strcmp(clntmessage, "q\n") || !strcmp(clntmessage, "Q\n")) { adr_sz = sizeof(from_adr); str_len = recvfrom(sock, servmessage, BUF_SIZE, 0, (struct sockaddr*)&from_adr, &adr_sz); printf("Message from server: %s", servmessage); if (!strcmp(servmessage, "bye\n")) break; else servmessage[str_len] = 0; printf("Message from server: %s", servmessage); continue; } adr_sz=sizeof(from_adr); str_len=recvfrom(sock, servmessage, BUF_SIZE, 0, (struct sockaddr*)&from_adr, &adr_sz); servmessage[str_len]=0; printf("Message from server: %s", servmessage); } close(sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
服务器UDP套接字代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 30 void error_handling(char *message); int main(int argc, char *argv[]) { int serv_sock; char send_message[BUF_SIZE]; char recv_message[BUF_SIZE]; int str_len; socklen_t clnt_adr_sz; struct sockaddr_in serv_adr, clnt_adr; if(argc!=2){ printf("Usage : %s <port>\n", argv[0]); exit(1); } serv_sock=socket(PF_INET, SOCK_DGRAM, 0); if(serv_sock==-1) error_handling("UDP socket creation error"); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=htonl(INADDR_ANY); serv_adr.sin_port=htons(atoi(argv[1])); if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1) error_handling("bind() error"); while(1) { str_len = recvfrom(serv_sock, recv_message, BUF_SIZE, 0, (struct sockaddr*)&clnt_adr, &clnt_adr_sz); if (!strcmp(recv_message, "q\n") || !strcmp(recv_message, "Q\n")) { sendto(serv_sock, "bye", 4, 0, (struct sockaddr*)&clnt_adr, clnt_adr_sz); printf("Message received from Client: %s", recv_message); break; } recv_message[str_len] = 0; printf("Message received from Client: %s", recv_message); fputs("Insert message: ", stdout); fgets(send_message, sizeof(send_message), stdin); sendto(serv_sock, send_message, strlen(send_message), 0, (struct sockaddr*)&clnt_adr, sizeof(clnt_adr)); } close(serv_sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
问题原因分析
- 客户端缩进逻辑错误:
if退出分支里的else未加花括号,导致只有servmessage[str_len] = 0;属于分支内容,后续代码逻辑混乱,出现重复接收、无效判断的情况。 - 服务器回复字符串不匹配:服务器收到退出指令时发送
"bye",但客户端判断的是"bye\n",字符串不匹配导致客户端无法正常退出,持续阻塞等待。 - 服务器地址长度参数错误:正常回复时,
sendto使用sizeof(clnt_adr)作为地址长度,而非recvfrom返回的实际地址长度clnt_adr_sz,虽IPv4下数值相同,但属于不规范写法,可能引发异常。 - 字符串终止时机错误:服务器在判断退出条件前未终止接收到的字符串,可能导致
strcmp判断失效;客户端在接收服务器回复后,部分场景未先终止字符串就打印判断,存在内存越界风险。
修复后的代码
修复后的客户端代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 30 void error_handling(char *message); int main(int argc, char *argv[]) { int sock; char clntmessage[BUF_SIZE]; char servmessage[BUF_SIZE]; int str_len; socklen_t adr_sz; struct sockaddr_in serv_adr, from_adr; if(argc!=3){ printf("Usage : %s <IP> <port>\n", argv[0]); exit(1); } sock=socket(PF_INET, SOCK_DGRAM, 0); if(sock==-1) error_handling("socket() error"); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=inet_addr(argv[1]); serv_adr.sin_port=htons(atoi(argv[2])); while(1) { fputs("Insert message(q or Q quit): ", stdout); fgets(clntmessage, sizeof(clntmessage), stdin); sendto(sock, clntmessage, strlen(clntmessage), 0, (struct sockaddr*)&serv_adr, sizeof(serv_adr)); if (!strcmp(clntmessage, "q\n") || !strcmp(clntmessage, "Q\n")) { adr_sz = sizeof(from_adr); str_len = recvfrom(sock, servmessage, BUF_SIZE, 0, (struct sockaddr*)&from_adr, &adr_sz); servmessage[str_len] = 0; printf("Message from server: %s", servmessage); if (!strcmp(servmessage, "bye\n")) break; continue; } adr_sz=sizeof(from_adr); str_len=recvfrom(sock, servmessage, BUF_SIZE, 0, (struct sockaddr*)&from_adr, &adr_sz); servmessage[str_len]=0; printf("Message from server: %s", servmessage); } close(sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
修复后的服务器代码
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define BUF_SIZE 30 void error_handling(char *message); int main(int argc, char *argv[]) { int serv_sock; char send_message[BUF_SIZE]; char recv_message[BUF_SIZE]; int str_len; socklen_t clnt_adr_sz; struct sockaddr_in serv_adr, clnt_adr; if(argc!=2){ printf("Usage : %s <port>\n", argv[0]); exit(1); } serv_sock=socket(PF_INET, SOCK_DGRAM, 0); if(serv_sock==-1) error_handling("UDP socket creation error"); memset(&serv_adr, 0, sizeof(serv_adr)); serv_adr.sin_family=AF_INET; serv_adr.sin_addr.s_addr=htonl(INADDR_ANY); serv_adr.sin_port=htons(atoi(argv[1])); if(bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr))==-1) error_handling("bind() error"); while(1) { clnt_adr_sz = sizeof(clnt_adr); str_len = recvfrom(serv_sock, recv_message, BUF_SIZE, 0, (struct sockaddr*)&clnt_adr, &clnt_adr_sz); recv_message[str_len] = 0; printf("Message received from Client: %s", recv_message); if (!strcmp(recv_message, "q\n") || !strcmp(recv_message, "Q\n")) { sendto(serv_sock, "bye\n", 5, 0, (struct sockaddr*)&clnt_adr, clnt_adr_sz); break; } fputs("Insert message: ", stdout); fgets(send_message, sizeof(send_message), stdin); sendto(serv_sock, send_message, strlen(send_message), 0, (struct sockaddr*)&clnt_adr, clnt_adr_sz); } close(serv_sock); return 0; } void error_handling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
关键修复点说明
- 客户端:修正
else分支缩进逻辑,去掉冗余代码;接收回复后先终止字符串再进行打印和判断。 - 服务器:初始化
recvfrom的地址长度变量;发送退出回复时使用带换行符的"bye\n";统一用clnt_adr_sz作为sendto的地址长度参数;接收消息后先终止字符串再判断退出条件。
内容的提问来源于stack exchange,提问作者허준영
相关产品推荐
相关产品推荐

