Reputation: 938
I'm trying to set up a simple communication scheme with a program I wrote using UDP. The program will set up a UDP socket in a nonblocking fashion, then use select() to read non blocking from the socket. Here are the contents of the program:
#include <stdio.h> /* Standard input/output definitions */
#include <stdlib.h> /* UNIX standard function definitions */
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <fcntl.h> /* File control definitions */
#include <signal.h>
#include <time.h>
#include <stdint.h> /* Standard integer types */
#include <string.h> /* String function definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/select.h>
int setupUdpSocketN(int *fd, uint16_t port_no)
{
struct sockaddr_in my_addr;
// Create UDP socket:
if ( (*fd = socket(AF_INET,SOCK_DGRAM,0)) < 0 )
return -1;
fcntl(*fd, F_SETFL, O_NONBLOCK);
// Bind socket:
memset((char *)&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
my_addr.sin_port = htons(port_no);
if ( bind(*fd, (struct sockaddr*)&my_addr, sizeof(my_addr)) == -1 ) {
close(*fd);
return -2;
}
return EXIT_SUCCESS;
}
int main(void) {
int fd, maxfd, nready;
size_t recvlen;
uint8_t buf[256];
fd_set rset;
setupUdpSocketN(&fd, 60000);
maxfd = fd + 1;
while(1) {
FD_ZERO(&rset);
FD_SET(fd, &rset);
if( (nready = select(maxfd, &rset, NULL, NULL, NULL)) < 0 ) {
printf("Error in select: %s\n", strerror(errno));
}
if( FD_ISSET(fd, &rset) ) {
recvlen = recv(fd, buf, 256, 0);
//recvlen = recvfrom(fd, buf, 256, 0, NULL, NULL);
for(int i = 0; i < recvlen; i++) {
printf("%02x ", buf[i]);
}
printf("\n");
}
}
return 0;
}
I then try and send this program binary data using echo, xxd, and netcat:
echo -ne "\xfe\x64\x32\x1a\xb0" | xxd -rp | nc -u localhost 60000
However, this program when run just blocks endlessly. I'm very new to socket programming and unsure how to proceed.
Upvotes: 2
Views: 879
Reputation: 4620
I think the main problem is not with your code but with how you call netcat. When I run your server and use nc -u localhost 60000
, I can reproduce your problem: The server is blocking indefinitely without receiving data. However when I force netcat to use IPv4, I can send data to the server successfully:
nc -4 -u localhost 60000
Alternatively, changing your code to use IPv6 and using netcat without the -4
flag also works:
struct sockaddr_in6 my_addr;
...
if ( (*fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0 )
return -1;
...
my_addr.sin6_family = AF_INET6;
my_addr.sin6_addr = in6addr_any;
my_addr.sin6_port = htons(port_no);
To show you how I debugged the problem: I first used netcat to start a UDP server, and send data to the server with a second netcat instance. This worked as expected. I then used strace
to inspect the system calls used by the netcat server. The strace
output shows netcat using IPv6:
...
socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
bind(3, {sa_family=AF_INET6, sin6_port=htons(60000), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = 0
...
Besides that, there are two miscellaneous problems:
The recv
function returns the number of bytes received or -1
in case of an error. For this reason, the type of the return value is ssize_t
, instead of size_t
. Since your code declares size_t recvlen
, when recv
returns -1
an integer underflow will occur and recv
will be the maximum possible value of type size_t
(e.g. ULLONG_MAX = 18446744073709551615
on my platform). This would then result in a buffer overrun, as buf
is much smaller.
Your echo
/xxd
call produces only zeros for me. I'm not sure if this intended. The -r
flag for xxd
enables "reverse" mode, where xxd
will attempt to parse a hexdump (e.g. as produced by xxd
without the -r
flag) and then reproduces the raw byte sequence that was the input to the original hexdump. But in your case, the echo
outputs raw binary data, not an encoded hexdump, hence this data cannot be parsed by xxd
in reverse mode.
Upvotes: 1