NetBSD-Users archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Non-blocking socket bug
I've come across an issue when testing networking code, which looks
like a bug. This is on NetBSD-8.1
When a listening socket is set non-blocking and we call accept() on
that socket, the new socket returned by accept() will also be
non-blocking.
I think this is a bug. I thought all new sockets/file descriptors would
be non-blocking by default. I've attached a test program to demonstrate
the issue.
On Linux:
$ ./a.out
Create server thread
Create client thread
Accept connection, fd=5, addr=127.0.0.1, port=47746
Connected socket is blocking
On NetBSD:
$ ./a.out
Create server thread
Create client thread
Accept connection, fd=5, addr=127.0.0.1, port=65534
Connected socket is non-blocking
Any ideas or comments?
Thanks.
#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <pthread.h>
#include <poll.h>
#define ADDR "127.0.0.1"
#define PORT 9999
/* Set fd blocking or non-blocking */
void set_fd_nonblock(int fd, bool set_flag)
{
int ret_int, fcntl_flags;
/* Get current flags */
fcntl_flags = fcntl(fd, F_GETFL, 0);
if (fcntl_flags == -1)
{
fprintf(stderr, "Error %s:%d: fcntl() with F_GETFL failed, %s\n",
__FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (set_flag)
{
/* Set non-blocking */
fcntl_flags |= O_NONBLOCK;
}
else
{
/* Set blocking */
fcntl_flags &= ~O_NONBLOCK;
}
ret_int = fcntl(fd, F_SETFL, fcntl_flags);
if (ret_int == -1)
{
fprintf(stderr, "Error %s:%d: fcntl() with F_SETFL failed, %s\n",
__FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
}
/* Check fd is non-blocking */
bool is_fd_nonblock(int fd)
{
int fcntl_flags;
/* Get current flags */
fcntl_flags = fcntl(fd, F_GETFL, 0);
if (fcntl_flags == -1)
{
fprintf(stderr, "Error %s:%d: fcntl() with F_GETFL failed, %s\n",
__FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
return (fcntl_flags & O_NONBLOCK);
}
/* Client thread */
static void *cli(void *arg)
{
struct sockaddr_in addr;
int conn_fd;
memset(&addr, 0, sizeof(addr));
if (inet_pton(AF_INET, ADDR, &(addr.sin_addr)) != 1)
{
fprintf(stderr, "Error %s:%d: inet_pton() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
conn_fd = socket(AF_INET, SOCK_STREAM, 0);
if (conn_fd < 0)
{
fprintf(stderr, "Error %s:%d: socket() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (connect(conn_fd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
fprintf(stderr, "Error %s:%d: connect() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
return NULL;
}
/* Server thread */
static void *srv(void *arg)
{
struct sockaddr_in addr, cli_addr;
socklen_t cli_addr_len;
char addr_str[INET6_ADDRSTRLEN];
int ret_int, listen_fd, conn_fd;
ssize_t ret_ssize;
struct pollfd pollfd_array[1];
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(PORT);
/* The usual - socket, bind, listen */
listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd < 0)
{
fprintf(stderr, "Error %s:%d: socket() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
{
fprintf(stderr, "Error %s:%d: bind() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (listen(listen_fd, 128) < 0)
{
fprintf(stderr, "Error %s:%d: listen() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
/* Set listening socket non-blocking */
set_fd_nonblock(listen_fd, true);
/* Set poll events */
pollfd_array[0].fd = listen_fd;
pollfd_array[0].events = (POLLIN | POLLRDNORM | POLLRDBAND | POLLPRI);
/* Block in poll until socket is ready for I/O */
ret_int = poll(pollfd_array, 1, -1);
if (ret_int < 0)
{
fprintf(stderr, "Error %s:%d: poll() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
/* Accept new connection */
cli_addr_len = sizeof(cli_addr);
conn_fd = accept(listen_fd, (struct sockaddr *)&cli_addr, &cli_addr_len);
if (conn_fd < 0)
{
fprintf(stderr, "Error %s:%d: accept() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
if (inet_ntop(AF_INET, &(cli_addr.sin_addr), addr_str, sizeof(addr_str)) == NULL)
{
fprintf(stderr, "Error %s:%d: inet_ntop() failed, %s\n", __FILE__, __LINE__, strerror(errno));
exit(EXIT_FAILURE);
}
fprintf(stdout, "Accept connection, fd=%d, addr=%s, port=%hu\n",
conn_fd, addr_str, ntohs(cli_addr.sin_port));
/* Check if connected socket is non-blocking or blocking */
if (is_fd_nonblock(conn_fd))
{
fprintf(stdout, "Connected socket is non-blocking\n");
}
else
{
fprintf(stdout, "Connected socket is blocking\n");
}
return NULL;
}
int main(void)
{
int ret_int;
pthread_t cli_tid, srv_tid;
fprintf(stdout, "Create server thread\n");
ret_int = pthread_create(&srv_tid, NULL, &srv, NULL);
if (ret_int != 0)
{
fprintf(stderr, "Error %s:%d: pthread_create() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
exit(EXIT_FAILURE);
}
sleep(1); /* Give server thread time to setup listening socket */
fprintf(stdout, "Create client thread\n");
ret_int = pthread_create(&cli_tid, NULL, &cli, NULL);
if (ret_int != 0)
{
fprintf(stderr, "Error %s:%d: pthread_create() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
exit(EXIT_FAILURE);
}
/* Wait for client to exit */
ret_int = pthread_join(cli_tid, NULL);
if (ret_int != 0)
{
fprintf(stderr, "Error %s:%d: pthread_join() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
exit(EXIT_FAILURE);
}
/* Wait for server to exit */
ret_int = pthread_join(srv_tid, NULL);
if (ret_int != 0)
{
fprintf(stderr, "Error %s:%d: pthread_join() failed, %s\n", __FILE__, __LINE__, strerror(ret_int));
exit(EXIT_FAILURE);
}
}
Home |
Main Index |
Thread Index |
Old Index