服务器套接字错误的IP和端口

当我写下面这样的代码时,对方报告的IP和端口是正确的:

int main(){ int server = socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(8080); addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(server,(struct sockaddr *)&addr,sizeof addr); listen(server,5); struct sockaddr_storage inc_adrs; socklen_t inc_len; int client = accept(server, (struct sockaddr *) &inc_adrs,&inc_len); struct sockaddr_in *inc_adr = (struct sockaddr_in *) &inc_adrs; char ip [INET_ADDRSTRLEN]; inet_ntop(AF_INET,&(inc_adr->sin_addr),ip,INET_ADDRSTRLEN); printf("Connection from %s port %d\n",ip,ntohs(inc_adr->sin_port)); return 0; } 

例如,我使用本地机器上的nc连接到它:

 #nc 127.0.0.1 8080 

而输出是:

 Connection from 127.0.0.1 port 55112 

但是,如果我只是把它们放在另一个块中,如“if”或“while”,则程序报告错误的IP和端口:

 int main(){ if (1){ int server = socket(AF_INET,SOCK_STREAM,0); struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(8080); addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(server,(struct sockaddr *)&addr,sizeof addr); listen(server,5); struct sockaddr_storage inc_adrs; socklen_t inc_len; int client = accept(server, (struct sockaddr *) &inc_adrs,&inc_len); struct sockaddr_in *inc_adr = (struct sockaddr_in *) &inc_adrs; char ip [INET_ADDRSTRLEN]; inet_ntop(AF_INET,&(inc_adr->sin_addr),ip,INET_ADDRSTRLEN); printf("Connection from %s port %d\n",ip,ntohs(inc_adr->sin_port)); return 0; } } 

示例输出:

 Connection from 253.127.0.0 port 32323 

我在linux x64上用“gcc code.c -o code”进行编译。 这是非常奇怪的,我不知道这是可能的。 任何想法?

好吧,貌似问题在于你如何使用man 2 accept

addrlen参数是一个值结果参数:调用者必须初始化它以包含由addr指向的结构的大小(以字节为单位) 在返回时,它将包含对等地址的实际大小。

如果提供的缓冲区太小,则返回的地址将被截断; 在这种情况下, addrlen将返回一个大于提供给该调用的值。

通过以下修复加上 SO_REUSEADDR,你的代码工作正常:

  struct sockaddr_storage inc_adrs; socklen_t inc_len = sizeof( inc_adrs ); 

此外,不要忘记检查socket结果值, bindlistenaccept

 ./a.out Connection from 127.0.0.1 port 11111 --- nc 127.0.0.1 8080 -p 11111 Connection from 192.168.0.1 port 11111 --- nc 192.168.0.1 8080 -p 11111 

你正在使用struct sockaddr_storage inc_adrs; accept后可能(取决于inc_len中的随机值):

  • 将完整的值复制到inc_adrs中
  • 将不完整的值复制到inc_adrs中
  • 如果inc_len恰好为零,切勿触碰inc_adrs。

例如在我的情况下, if inc_len是零。 inc_adrs保持不变,并包含一些垃圾:

 inc_adrs = {ss_family = 0, __ss_align = 234832925920, __ss_padding = "@\300\377\377\377\177\000\000G\004I\255\066\000\000\000X... 

这个垃圾取决于编译器如何在堆栈中分配值以及之前的代码(包括glibc)将如何处理堆栈。

例如相同的值没有if

 1: inc_len = 54 <<--- this one is unitialized!!!! 2: inc_adrs = {ss_family = 49240, __ss_align = 4294967296, __ss_padding = "\000\000\000\000\000\000\000\000\034\00 

正如你所看到的inc_len恰好是54这是足够的accept保存sockaddr_in,因此这种情况下,就好像一切正​​常。

ifnot-if情况之间的区别:

 - leaq -52(%rbp), %rdx - leaq -208(%rbp), %rcx + leaq -180(%rbp), %rdx + leaq -176(%rbp), %rcx movl -4(%rbp), %eax movq %rcx, %rsi movl %eax, %edi call accept 

所以它只是偶然的正确工作。 你可以random模拟相同的:

 #include <stdlib.h> srand(time(NULL)); ... socklen_t inc_len = random() % (sizeof(struct sockaddr_in)*2) 

这将填充inc_adrs 50%的时间,5%不接触它。