Is it possible () + non-blocking write () on a blocking channel or socket?

The situation is that I have a blocking pipe or socket fd that I want to access write()

without blocking, so I do it first select()

, but that still doesn't guarantee that write()

it won't block.

Here's the data I've collected. Even if select()

indicates that writing is possible, writing can more than PIPE_BUF

block bytes. However, writing no more than PIPE_BUF

bytes does not seem to block, but this is not provided by the POSIX spec .

This only indicates the behavior of atoms. The Python documentation (!) States that:

The files that are reported as ready for writing select()

, poll()

or similar interfaces in this module is guaranteed not to be blocked when writing PIPE_BUF

bytes. This value is guaranteed by POSIX as a minimum 512

.

In the following test program, set BUF_BYTES

to tell 100000

to lock write()

on Linux, FreeBSD or Solaris after a successful selection. I'll assume named pipes have similar behavior to anonymous pipes.

Unfortunately, this can happen with socket blocking too. Call test_socket()

in main()

and use is pretty big BUF_BYTES

( 100000

okay here too). It is not clear if there is a safe buffer size, for example PIPE_BUF

for sockets.

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <limits.h>
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>

#define BUF_BYTES PIPE_BUF
char buf[BUF_BYTES];

int
probe_with_select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds)
{
    struct timeval timeout = {0, 0};
    int n_found = select(nfds, readfds, writefds, exceptfds, &timeout);
    if (n_found == -1) {
        perror("select");
    }
    return n_found;
}

void
check_if_readable(int fd)
{
    fd_set fdset;
    FD_ZERO(&fdset);
    FD_SET(fd, &fdset);
    printf("select() for read on fd %d returned %d\n",
           fd, probe_with_select(fd + 1, &fdset, 0, 0));
}

void
check_if_writable(int fd)
{
    fd_set fdset;
    FD_ZERO(&fdset);
    FD_SET(fd, &fdset);
    int n_found = probe_with_select(fd + 1, 0, &fdset, 0);
    printf("select() for write on fd %d returned %d\n", fd, n_found);
    /* if (n_found == 0) { */
    /*     printf("sleeping\n"); */
    /*     sleep(2); */
    /*     int n_found = probe_with_select(fd + 1, 0, &fdset, 0); */
    /*     printf("retried select() for write on fd %d returned %d\n",  */
    /*            fd, n_found); */
    /* } */
}

void
test_pipe(void)
{
    int pipe_fds[2];
    size_t written;
    int i;
    if (pipe(pipe_fds)) {
        perror("pipe failed");
        _exit(1);
    }
    printf("read side pipe fd: %d\n", pipe_fds[0]);
    printf("write side pipe fd: %d\n", pipe_fds[1]);
    for (i = 0; ; i++) {
        printf("i = %d\n", i);
        check_if_readable(pipe_fds[0]);
        check_if_writable(pipe_fds[1]);
        written = write(pipe_fds[1], buf, BUF_BYTES);
        if (written == -1) {
            perror("write");
            _exit(-1);
        }
        printf("written %d bytes\n", written);
    }
}

void
serve()
{
    int listenfd = 0, connfd = 0;
    struct sockaddr_in serv_addr;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&serv_addr, '0', sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(5000);

    bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    listen(listenfd, 10);

    connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);

    sleep(10);
}

int
connect_to_server()
{
    int sockfd = 0, n = 0;
    struct sockaddr_in serv_addr;

    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket");
        exit(-1);
    }

    memset(&serv_addr, '0', sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(5000);

    if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
        perror("inet_pton");
        exit(-1);
    }

    if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        perror("connect");
        exit(-1);
    }

    return sockfd;
}

void
test_socket(void)
{
    if (fork() == 0)  {
        serve();
    } else {
        int fd;
        int i;
        int written;
        sleep(1);
        fd = connect_to_server();

        for (i = 0; ; i++) {
            printf("i = %d\n", i);
            check_if_readable(fd);
            check_if_writable(fd);
            written = write(fd, buf, BUF_BYTES);
            if (written == -1) {
                perror("write");
                _exit(-1);
            }
            printf("written %d bytes\n", written);
        }
    }
}

int
main(void)
{
    test_pipe();
    /* test_socket(); */
}

      

+3


source to share


2 answers


If you don't want to send one byte at a time when select () says fd is ready to write, there is no way to know how much you can send, and even then it is theoretically possible (at least in the documentation, if not real world) for a selection to say it is ready for writing, and then a condition for changing in time between select () and write ().



Non-blocking sends is the solution here and you don't need to change the file descriptor to non-blocking mode to send one message to a non-blocking form if you change with write () to send (). The only thing you need to change is add the MSG_DONTWAIT flag to the send call and this will cause one to send a non-blocking without changing your socket properties. You don't even need to use select () at all in this case, since the send () call will give you all the information you need in the return code - if you get a return code of -1 and errno is EAGAIN or EWOULDBLOCK, then you know you are no longer you can send.

+1


source


The Posix section you are citing clearly states:

[for pipes] If the O_NONBLOCK flag is free, the write request may block the thread, but should return nbyte on normal completion.

[for streams that presumably include stream sockets] If O_NONBLOCK is clear and STREAM cannot receive data (STREAM write queue full due to internal flow control conditions), write () blocks until data is received.



Thus, the Python documentation you provided can only apply to non-blocking mode. But since you are not using Python, this has nothing to do with it.

+1


source







All Articles