当我编写如下代码时,报告的对等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”编译它。 这很奇怪,我不知道它是怎么可能的。有什么想法吗?
答案 0 :(得分:2)
好的,看起来问题在于你如何使用man 2 accept
:
addrlen
参数是一个value-result参数:调用者必须初始化它以包含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
碰巧为零,例如,在我的情况下,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%不会触及它。