1

I read() from clients connected to the server, and at the same time, I let select() be aware of data coming from a FIFO. Right now, when data is written to the FIFO, selects writes all data to the clients but keeps returning as if it was "ready-to-read". So the next read is set to -1 and errno == EAGAIN. It does this until it reaches fdmax. It works okay though.

But, why do I keep getting EAGAIN? and is there a better way to handle this? or is this the proper way?

Note: I'm passing O_RDWR|O_NONBLOCK so it can also keep read()ing data sent by clients and not just the FIFO.

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

#define PORT "9034"
int main(void) {
    fd_set master, read_fds;
    int fdmax, listener, newfd, sbytes, yes=1, i, j, rv; 
    struct sockaddr_storage remoteaddr; // client address
    socklen_t addrlen;
    char buf[256] = {0}, remoteIP[INET6_ADDRSTRLEN];
    struct addrinfo hints, *ai;

    FD_ZERO(&master);
    FD_ZERO(&read_fds);
    int fifo;
    if ((mkfifo("/tmp/fifo", 0666)) < 0)
        perror(strerror(errno));
    if ((fifo = open("/tmp/fifo", O_RDWR|O_NONBLOCK)) < 0)  
        perror(strerror(errno));

    // get us a socket and bind it
    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    rv = getaddrinfo(NULL, PORT, &hints, &ai);
    listener = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
    setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
    bind(listener, ai->ai_addr, ai->ai_addrlen);
    freeaddrinfo(ai);
    listen(listener, 10);

    FD_SET (fifo, &master);
    FD_SET(listener, &master);
    fdmax = listener;
    for (;;) {
        read_fds = master;
        if (select(fdmax + 1, &read_fds, NULL, NULL, NULL) == -1) exit(4);

        for (i = 0; i <= fdmax; i++) {
            if (FD_ISSET(i, &read_fds)) {
                if (i == listener) {
                    addrlen = sizeof remoteaddr;
                    newfd = accept(listener, (struct sockaddr * ) & remoteaddr, & addrlen);
                    FD_SET(newfd, & master);
                    if (newfd > fdmax)
                        fdmax = newfd;
                } else if (i != fifo) {
                    recv(i, buf, sizeof buf, 0); 
                }   
            }
            if (FD_ISSET(fifo, &read_fds)) {
                sbytes = read (fifo, buf, sizeof (buf));
                if (sbytes == -1 && errno == EAGAIN)
                    continue;
                for(j = 0; j <= fdmax; j++) {
                    // send to everyone!
                    if (FD_ISSET(j, &master)) {
                        if (j != listener && j != i && j != fifo) {
                            if (send(j, buf, sbytes, 0) == -1) {
                                perror("send");
                            }   
                        }   
                    }   
                }   
            }   
        }   
    }   
    return 0;
}
Pete Darrow
  • 455
  • 5
  • 20
  • `fdmax = listener;` is dubious. You should actually evaluate which is the greater. You need to check the result of each `recv()`. You're not doing that in the socket case. Specifically, it could be zero, which means the peer has disconnected and you should close the socket and remove it from `read_fds` and `master`. – user207421 Apr 19 '16 at 02:32
  • Thanks. For simplicity, I removed a lot of the checks. I'll make sure to do add it though. – Pete Darrow Apr 19 '16 at 02:36
  • as for `fdmax`, if it helps, you can have a look at [this piece of code](https://github.com/boazsegev/c-server-tools/blob/b27548898dea4b0e8fb42b439dd7f50d7c0e7073/src/lib-server.c#L1729-L1749) that checks the maximum file limit for the process (as well as tries to maximize it up to the OS's hard limit). – Myst Apr 19 '16 at 03:19
  • @Myst thanks for posting this. – Pete Darrow Apr 19 '16 at 03:59

1 Answers1

1
for (i = 0; i <= fdmax; i++) {

Here you are iterating the read_fds.

if (FD_ISSET(fifo, &read_fds)) {

Here you are testing whether fifo is readable. Every time around the loop. The first time, you read something, and send it; the next time around the loop, nothing has happened to the FIFO, but the condition above still holds, so you read again and get EAGAIN.

This if block should be outside and below the loop.

user207421
  • 305,947
  • 44
  • 307
  • 483