【问题标题】:getaddrinfo, bind, localhost and INADDR_ANYgetaddrinfo、bind、localhost 和 INADDR_ANY
【发布时间】:2020-09-24 00:59:29
【问题描述】:

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

我知道我应该以某种方式将INADDR_ANY 传递给 bind() 让它接受来自两者的数据包。我我需要将char *service=NULL 传递给getaddrinfo 以便它分配一个端口。不过不知道是不是这样,getaddrinfonode参数怎么办。

根据getaddrinoman页面,nodeservice不能都为NULL,但INADDR_ANY只会在(1)hints.ai_flags=AI_PASSIVE时设置在返回的socket地址中(2)node设置为NULL。我对这种冲突感到困惑.. 而且由于“有一个操作系统分配的端口”是分配要求,我猜我不能更改service

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

欢迎评论!

【问题讨论】:

  • 如果你只打电话给listen而不先打电话给bind会发生什么?
  • getaddrinfo 没有分配任何东西。它只查找知名端口。没有法律规定在没有先调用getaddrinfo 的情况下调用bind() 绑定到特定端口是非法的。您可以随意调用bind() 绑定到INADDR_ANY 上的特定端口。结束。

标签: c++ sockets udp getaddrinfo


【解决方案1】:

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

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

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

  • node 设置为"0.0.0.0" 并将service 设置为NULLAI_PASSIVE 被忽略)

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

任何这些组合都会导致getaddrinfo() 返回指向sockaddr_in 的指针,该指针将其sin_addr 设置为INADDR_ANY,并将其sin_port 设置为0

然后您可以使用sockaddr_inbind()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);
}

【讨论】:

  • 很好的答案!非常感谢!还有一个问题:这是否意味着在编写服务器端时,我们真的不需要getaddrino,因为我们知道主机IP(INADDR_ANY)和端口(任何可用的),并且只需要getaddrinfo在客户端根据主机名(例如www.google.com)确定IP
  • @Crystina 通常,是的。尽管 getaddrinfo 确实在服务器端有使用,例如将 AI_PASSIVE 与特定 IP 或端口组合时,和/或与 AF_UNSPEC 组合以创建 IPv4 和 IPv6 的绑定套接字。
猜你喜欢
  • 2018-03-19
  • 1970-01-01
  • 1970-01-01
  • 2016-09-19
  • 2018-03-04
  • 1970-01-01
  • 2017-07-21
  • 1970-01-01
  • 2011-08-22
相关资源
最近更新 更多