【问题标题】:Wrong IP and port from server socket服务器套接字的 IP 和端口错误
【发布时间】:2015-08-21 17:15:03
【问题描述】:

当我编写如下代码时,报告的对端的 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”编译它。 这很奇怪,我不知道这怎么可能。有什么想法吗?

【问题讨论】:

  • 不需要使用 sockaddr_storage,立即使用 sockaddr_in。在使用 inet_ntop 之前检查该结构内的值。
  • 另请阅读:stackoverflow.com/questions/20130415/… 不要使用 inet_ntop。
  • 1.使用 SO_REUSEADDR:stackoverflow.com/questions/24194961/… 2. 检查来自 socketbindlistenaccept 的返回值。如果其中之一失败,您将在代码中得到垃圾。
  • @PhilipStuyck 我做了你说的问题仍然存在。 “if block”改变程序的行为似乎是不可能的。为什么在“if”中会出现问题
  • @CS。所以这不是真正的代码吗?那我就不费心调试了。

标签: c++ c linux sockets gcc


【解决方案1】:

好的,看来问题在于你如何使用man 2 accept

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

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

通过以下修复plus SO_REUSEADDR,您的代码可以正常工作:

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

另外,不要忘记检查来自socketbindlistenaccept 的结果值。

./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

您可能在accept 之后使用struct sockaddr_storage inc_adrs;(取决于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% 的时间不会碰它。

【讨论】:

  • 感谢您的回复。但我没有得到我想要的答案。正如我在 cmets 中所说,我可以通过其他方式解决问题,但我想知道为什么会发生这种情况。为什么此代码在“if”或任何其他范围(如“while”)之外有效,但不在内部。因为这一定不会造成问题。我想知道这里发生了什么
  • 这就是调试器的用途。您是否尝试过在调用accept() 之前调试代码并检查inc_len 的值,并在accept() 退出后检查inc_adrs 的内容和inc_len 的值?您是否尝试检查您传递给inet_ntop() 的内容?垃圾进,垃圾出。
  • @RemyLebeau 不,我没有。但它怎么能依赖于“if”之外的单个“if”块,它总是能正常工作?
  • 在你编辑你的答案后,我同意更多,我怀疑没有“如果”大多数时候(如果不是总是)堆栈以一种 inc_len 获得“可接受”值的方式填充!
  • @fukanchik 是的,唯一的问题是是什么导致程序不随机执行?正如我所说,每次我在代码位于 if 块之外时得到正确的输出。似乎它需要更多的装配分析。谢谢
猜你喜欢
  • 2018-04-24
  • 1970-01-01
  • 1970-01-01
  • 2012-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-23
  • 1970-01-01
相关资源
最近更新 更多