当我写下面这样的代码时,对方报告的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
结果值, bind
, listen
, accept
。
./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_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,因此这种情况下,就好像一切正常。
if
和not-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%不接触它。