为什么无法绑定到指定IP地址的端口,但它与本地主机配合良好?

Why cannot bind to port with specified iP address, but it works well with localhost?

我正在创建一个服务器,如果一个客户端向服务器发送消息,它会向所有客户端发送消息。我在我的计算机中使用 telnet 和其他两个客户端测试了我的服务器。如果我在函数 getaddrinfo 的第一个参数中使用 "localhost" 作为值,它会起作用。但是当我用我的 ip 地址替换它时,我通过在 google 上搜索获得该地址,它无法将套接字绑定到端口。为什么会这样?

这在我测试 telnet 时效果很好。

if ((rv = getaddrinfo("localhost", PORT, &hints, &ai)) != 0) {
    fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
    exit(1);
}

for(p = ai; p != NULL; p = p->ai_next) {
    listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if (listener < 0) {
        continue;
    }

    // lose the pesky "address already in use" error message
    setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

    if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
        close(listener);
        continue;
    }

    break;
}

如果我指定了 IP 地址,程序将无法绑定 注意:我用 "xx.xxx.xxx.xxx"

替换了我的 IP 地址
if ((rv = getaddrinfo("xx.xxx.xxx.xxx", PORT, &hints, &ai)) != 0) { 
    fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
    exit(1);
}

最不寻常的是想要绑定到 "localhost" 或您的外部 IP 地址。这个时代的大多数计算机都没有外部 IP 地址(Google 看到的那个),因为它们和互联网之间有一个路由器和一个隔离区。如果您想将 LAN 可见的服务公开到 Internet,则需要将路由器配置为转发连接,这是一个更适合 Stack Overflow 网络变体的主题。

提供您的 hints.ai_flags = AI_PASSIVE,您应该能够使用 NULL 代替 "localhost",并且 getaddrinfo 将 return 列出所有合适的接口你可以绑定。如果您的 hints.ai_flags 不包含 AI_PASSIVE,则无法保证您可以绑定 returned 接口。如果您在这里选择使用字符串,那么 AI_PASSIVE 标志将被忽略...

在您的代码中,您正在一个接一个地创建多个套接字,在创建下一个套接字时丢弃之前创建的套接字...这是资源泄漏,也可能是导致您注意到的问题的原因。也许您打算将多个套接字描述符存储到一个数组中,并一次绑定一个?

完成 ai 后不要忘记 freeaddrinfo。在下面的示例中,我使用 PF_UNSPECSOCK_STREAM 来监听 IPV4 和 IPV6 接口(以及任何其他流类型),但可以更改这些以绑定到其他类型的地址.

#define BIND_ADDR NULL
#define BIND_PORT "1234"

struct addrinfo *ai;
int rv = getaddrinfo(BIND_ADDR,
                     BIND_PORT,
                     &(struct addrinfo){ .ai_family = PF_UNSPEC,
                                         .ai_socktype = SOCK_STREAM,
                                         .ai_flags = AI_PASSIVE },
                     &ai)
if (rv != 0) {
    fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
    exit(1);
}

size_t ai_size = 0;
for (struct addrinfo *a = ai; a != NULL; a = a->ai_next) {
     ai_size++;
}

int socket[ai_size] = { 0 };
size_t x = 0;
for (struct addrinfo *a = ai; a != NULL; a = a->ai_next) {
     socket[x++] = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
     /* ... */
}

freeaddrinfo(ai);
/* ... */