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

C++ 服务器套接字的IP和端口错误,c++,c,linux,sockets,gcc,C++,C,Linux,Sockets,Gcc,当我编写如下代码时,报告的对等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,(stru

当我编写如下代码时,报告的对等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
我在Linuxx64上用“gcc code.c-o代码”编译它。
这很奇怪,我不知道怎么可能。有什么想法吗?

好的,看来问题在于如何使用
man2 accept

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

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

使用以下修复程序,您的代码可以正常工作:

    struct sockaddr_storage inc_adrs;
    socklen_t inc_len = sizeof( inc_adrs );
另外,不要忘记检查来自
套接字
绑定
侦听
接受
的结果值

./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\u storage inc\u adrs
之后接受
可能(取决于inc_len中的随机值):

  • 将全部价值复制到inc_ADR中
  • 将不完整的值复制到inc_ADR中
  • 如果
    inc\u len
    恰好为零,则绝对不要接触inc\u adr
例如,在我使用
的情况下,如果
inc\u len
为零<代码>inc_ADR
保持不变,并包含一些垃圾:

inc_adrs = {ss_family = 0, __ss_align = 234832925920,
            __ss_padding = "@\300\377\377\377\177\000\000G\004I\255\066\000\000\000X...
这种垃圾取决于编译器如何在堆栈上分配值,以及以前的代码(包括glibc)将对堆栈执行什么操作

例如,如果
,则不带
的相同值:

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
因此,它只有在偶然的情况下才能正常工作。您可以使用
随机
模拟相同的情况:

#include <stdlib.h>

srand(time(NULL));
...
socklen_t inc_len = random() % (sizeof(struct sockaddr_in)*2)
#包括
srand(时间(空));
...
socklen\u t inc\u len=random()%(sizeof(struct sockaddr\u in)*2)

这将填充
inc\u ADR
50%的时间,5%的时间不接触它。

无需使用sockaddr\u存储,请立即在中使用sockaddr\u。在使用inet\u ntop之前,请检查该结构中的值。还要阅读以下内容:不要使用inet\u ntop.1。使用SO_REUSEADDR:2。检查来自
套接字
绑定
侦听
接受的返回值。如果其中一个失败了,你就会得到代码中的垃圾。@PhilipStuyck我按照你说的做了,问题仍然存在。“if块”改变程序的行为似乎是不可能的。为什么在“if”@CS中出现问题。所以这不是真正的代码?那我就不用麻烦调试了。谢谢回复。但是我没有得到我想要的答案。正如我在评论中所说,我可以用其他方法解决这个问题,但我想知道为什么会发生这种情况。为什么此代码在“if”或任何其他范围(如“while”)之外工作,而不是在内部工作。因为这不应该成为问题。我想知道这里发生了什么,这就是调试器的用途。您是否尝试过在调用
accept()
之前调试代码并检查
inc\u len
的值,以及在
accept()
退出后检查
inc\u len
的内容和值?您是否尝试检查传递给
inet\u ntop()
的内容?垃圾进来,垃圾出去。@RemyLebeau不,我没有。但是,它如何能够依赖于单个“如果”块,而不是“如果”块,它始终正常工作?在您编辑您的答案后,我更同意您的看法,并且我怀疑,如果没有“如果”,大多数情况下(如果不总是),堆栈的填充方式会使inc_len获得一个“可接受”的值@fukanchik是的,唯一的问题是什么导致程序不随机执行?正如我所说,每次当代码在if块之外时,我都会得到正确的输出。似乎它需要更多的组装分析。非常感谢。
#include <stdlib.h>

srand(time(NULL));
...
socklen_t inc_len = random() % (sizeof(struct sockaddr_in)*2)