getaddrinfo、绑定、localhost 和 INADDR_ANY
getaddrinfo, bind, localhost and INADDR_ANY
我正在学习套接字编程,对此还很陌生...
我的问题是,我想编写一个 UDP 服务器,它能够通过 OS 分配的套接字从远程服务器和本地主机接收数据包。
我知道我应该以某种方式将 INADDR_ANY
传递给 bind() 以让它接受来自两者的数据包。我 猜测 我需要将 char *service=NULL
传递给 getaddrinfo
以便它分配一个端口。但是,我不知道如何处理 getaddrinfo
.
的 node
参数
根据getaddrino
man页面,node
和service
不能同时为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()
手册页是正确的。 node
和 service
参数不能同时为 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_NUMERICSERV
(AI_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);
}
我正在学习套接字编程,对此还很陌生... 我的问题是,我想编写一个 UDP 服务器,它能够通过 OS 分配的套接字从远程服务器和本地主机接收数据包。
我知道我应该以某种方式将 INADDR_ANY
传递给 bind() 以让它接受来自两者的数据包。我 猜测 我需要将 char *service=NULL
传递给 getaddrinfo
以便它分配一个端口。但是,我不知道如何处理 getaddrinfo
.
node
参数
根据getaddrino
man页面,node
和service
不能同时为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()
手册页是正确的。 node
和 service
参数不能同时为 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_NUMERICSERV
(AI_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);
}