getaddrinfo、绑定、localhost 和 INADDR_ANY

getaddrinfo, bind, localhost and INADDR_ANY

我正在学习套接字编程,对此还很陌生... 我的问题是,我想编写一个 UDP 服务器,它能够通过 OS 分配的套接字从远程服务器和本地主机接收数据包。

我知道我应该以某种方式将 INADDR_ANY 传递给 bind() 以让它接受来自两者的数据包。我 猜测 我需要将 char *service=NULL 传递给 getaddrinfo 以便它分配一个端口。但是,我不知道如何处理 getaddrinfo.

node 参数

根据getaddrino man页面,nodeservice不能同时为NULL,但INADDR_ANY只会在返回的套接字中设置当 (1) hints.ai_flags=AI_PASSIVE(2) node 设置为 NULL 时的地址。我对这种冲突感到困惑.. 而且由于“有一个 OS-assigned 端口”是分配要求,我猜我无法更改 service

我读到 INADDR_ANY 基本上是 0.0.0.0,这是否意味着我可以将此字符串作为 node 传递?

欢迎任何评论!

分配随机可用端口不是getaddrinfo()。当请求端口 0 时,bind() 会这样做。

getaddrinfo() 手册页是正确的。 nodeservice 参数不能同时为 NULL。您可以将节点设置为 NULL 以获取 INADDR_ANY - 或者 - 您可以将 service 设置为 NULL 以获取端口 0。但是,要获得两者,您必须要么:

  • node设置为NULL,将service设置为"0",将hints.ai_flags设置为AI_PASSIVE | AI_NUMERICSERV

  • node设置为"0.0.0.0",将service设置为NULL(忽略AI_PASSIVE

  • node设置为"0.0.0.0",将service设置为"0",将hints.ai_flags设置为AI_NUMERICSERVAI_PASSIVE 被忽略)

这些组合中的任何一个都会导致 getaddrinfo() 到 return 一个指向 sockaddr_in 的指针,它的 sin_addr 设置为 INADDR_ANY 并且它的 sin_port 设置为 0.

然后您可以 bind() 使用 sockaddr_in,并且 bind() 将选择一个随机可用端口,之后您可以使用 getsockname() 检索该端口。

int server_fd = -1;

struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;

struct addrinfo *result;
if( getaddrinfo(NULL, "0", &hints, &result) != 0 )
{
    // error handling...
}
else if( (server_fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol)) < 0 )
{
    // error handling...
    freeaddrinfo(result);
}
else if( bind(server_fd, result->ai_addr, result->ai_addrlen) < 0 )
{
    // error handling...
    close(server_fd);
    freeaddrinfo(result);
}
else
{
    freeaddrinfo(result);

    // use server_fd as needed...

    struct sockaddr_in bound_addr;
    socklen_t addrlen = sizeof(bound_addr); 
  
    if( getsockname(server_fd, (struct sockaddr*)&bound_addr, &addrlen) < 0 )
    {
        // error handling...
    } 
    else
    {
        // use ntohs(bound_addr.sin_port) as needed...
    }

    ...

    close(server_fd);
}

或者,由于您确切地知道自己想要 bind() 什么,您可以简单地忽略 getaddrinfo() 并手动填充 sockaddr_in

int server_fd;

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = 0;

if( (server_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 )
{
    // error handling...
}
else if( bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0 )
{
    // error handling...
    close(server_fd);
}
else
{
    // use server_fd as needed...

    struct sockaddr_in bound_addr;
    socklen_t addrlen = sizeof(bound_addr); 
  
    if( getsockname(server_fd, (struct sockaddr*)&bound_addr, &addrlen) < 0 )
    {
        // error handling...
    } 
    else
    {
        // use ntohs(bound_addr.sin_port) as needed...
    }

    ...

    close(server_fd);
}