I'm learning socket programing and pretty new to this... My question is, I want to write a UDP server that is able to receive packets from both remote server and local host via a OS-assigned socket.
I know I should somehow pass INADDR_ANY
to bind() to let it accept packet from both. and I guess I need to pass char *service=NULL
to getaddrinfo
so that it will assign a port. However, I don't know if so what to do with the node
parameter of getaddrinfo
.
According to getaddrino
man page, node
and service
cannot both be NULL, yet the INADDR_ANY
will only be set in the returned socket address when (1) hints.ai_flags=AI_PASSIVE
and (2) node
is set to NULL. I'm confused with this confliction..
And since "have an OS-assigned port" is the assignment requirement, I can't change the service
I guess.
I read that INADDR_ANY is basically 0.0.0.0
, does that mean I can just pass this string as node
?
Any comment is welcomed!
It is not getaddrinfo()
that assigns a random available port. It is bind()
that does so when port 0 is requested.
The getaddrinfo()
man page is correct. The node
and service
parameters cannot both be NULL
at the same time. You can set node to NULL
to get INADDR_ANY
- or - you can set service
to NULL
to get port 0. But, to get both, you will have to either:
set node
to NULL
, service
to "0"
, and hints.ai_flags
to AI_PASSIVE | AI_NUMERICSERV
.
set node
to "0.0.0.0"
and service
to NULL
(AI_PASSIVE
is ignored)
set node
to "0.0.0.0"
, service
to "0"
, and hints.ai_flags
to AI_NUMERICSERV
(AI_PASSIVE
is ignored)
Any of those combinations will cause getaddrinfo()
to return a pointer to a sockaddr_in
that has its sin_addr
set to INADDR_ANY
and its sin_port
set to 0
.
You can then bind()
using that sockaddr_in
, and bind()
will choose a random available port, which you can retrieve using getsockname()
afterwards.
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);
}
Or, since you know exactly what you want to bind()
to, you can simply ignore getaddrinfo()
and populate a sockaddr_in
manually:
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);
}