C语言Socket编程:首次accept()返回客户端地址全为0的问题
问题:多进程Socket服务器首次accept()无法获取正确客户端地址
给出的多进程Socket服务器代码如下:
#include <sys/socket.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main(){ struct sockaddr_in svraddr; int clisock; pid_t pid; ssize_t nlen; struct sockaddr_in cliaddr; socklen_t cliaddrlen = sizeof(sockaddr_in); char buf[BUFSIZ]; svraddr.sin_family=AF_INET; svraddr.sin_port=htons(49510); svraddr.sin_addr.s_addr=htonl(INADDR_ANY); int serversock = socket(PF_INET,SOCK_STREAM,0); if(serversock==-1){ perror("socket()"); exit(1); } if(bind(serversock,(struct sockaddr*)&svraddr,sizeof(svraddr))==-1){ perror("bind()");exit(1); } if(listen(serversock,10)==-1){ perror("listen()");exit(1); } while(1){ clisock = accept(serversock,(struct sockaddr*)&cliaddr,&cliaddrlen); switch(pid=fork()){ case -1: perror("fork()");exit(1); case 0: close(serversock); printf("CONN: client %s:%d\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port)); while((nlen=read(clisock,buf,BUFSIZ))!=0){ perror("read"); printf("RECV: srcport %d, %s\n",ntohs(cliaddr.sin_port),buf); write(clisock,"received successfully",22); } close(clisock); printf("DISCONN: client %d disconnected\n",ntohs(cliaddr.sin_port)); exit(0); break; default: close(clisock); } } }
服务器运行后输出如下:
CONN: client 0.0.0.0:0 DISCONN: client 0 disconnected CONN: client 127.0.0.1:57078 DISCONN: client 57078 disconnected
可以看到首次调用accept()时,客户端地址显示为0.0.0.0:0,后续调用正常,且Socket I/O功能不受影响,即使memset初始化cliaddr也无法解决问题。
问题原因与解决方案
核心问题
问题出在**cliaddrlen变量的维护上**。POSIX标准规定,accept()的第三个参数是传入传出参数:
- 调用
accept()前,必须将该参数设置为客户端地址缓冲区(即cliaddr)的大小,告诉内核缓冲区的容量; - 调用完成后,内核会将该参数修改为实际写入的地址结构的长度。
你的代码中cliaddrlen仅在main函数开头初始化一次,首次调用accept()后,内核会修改它的值。后续循环调用accept()时,使用的是被修改后的cliaddrlen,这会导致内核无法正确识别缓冲区大小,进而首次调用时无法正确填充客户端地址信息(部分系统内核的行为差异会导致此问题显现)。
修复方案
将cliaddrlen的初始化放到while循环内部,确保每次调用accept()前都重置为缓冲区的正确大小:
while(1){ socklen_t cliaddrlen = sizeof(struct sockaddr_in); // 每次循环重置缓冲区大小 clisock = accept(serversock,(struct sockaddr*)&cliaddr,&cliaddrlen); switch(pid=fork()){ // 其余代码保持不变 } }
额外优化点
代码中read()的循环逻辑存在小问题:无论read()是否成功,都会调用perror("read"),这会在read()成功时打印冗余的read: Success信息。建议修改为仅在read()返回-1(出错)时调用perror:
while((nlen=read(clisock,buf,BUFSIZ))>0){ printf("RECV: srcport %d, %s\n",ntohs(cliaddr.sin_port),buf); write(clisock,"received successfully",22); } if(nlen == -1){ perror("read"); }
内容的提问来源于stack exchange,提问作者Thxx
相关产品推荐
相关产品推荐

