0

When the send() buffer is full, I set a flag in the write_fds for the given socket and the next iteration tries to send it by checking that write_fds is set.

It works fine setting the flag in write_fds for one socket. But, if I do FD_SET(i, write_fds) for multiple sockets, only the last socket appears as set and only the last socket gets the data.

If I do write_fds = master; right before select(2), then all of them appear as set and all unsent data is sent to all sockets, but select always returns.

I'm sure I must be doing something wrong.

FD_ZERO(&master);
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);

// socket setting...
// bind...
// listen...

FD_SET(listener, &master);
fdmax = listener; // so far, it's this one
for(;;){
    read_fds = master; // copy it
    // write_fds = master;
    select(fdmax+1, &read_fds, &write_fds, NULL, NULL);

    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;
                fcntl(newfd, F_SETFL, fcntl(newfd, F_GETFL, 0) | O_NONBLOCK);
            }else{
                // do some reading and then... send
                sent = send(i, buf, len, 0);
                if ((sent < 0 || (sent == -1 && errno == EWOULDBLOCK))
                    FD_SET(i, &write_fds);
            }  
        }  
        else if(FD_ISSET(i, &write_fds) && i != listener){
            if((sent = send(i, buf, len - sent, 0)) == len - sent)
                FD_CLR(i, &write_fds);
        }  
    }  
}
Peter D
  • 47
  • 5
  • If you use `gcc` compile it with warnings enabled `-Wall -Wextra`, this way it finds more issues at compile time. – Maxim Egorushkin Jun 15 '16 at 14:46
  • 4
    Note that you need to reconstruct the FD sets on each iteration; the call to `select` modifies the input/output parameters, destroying what you had set up. See [Is it necessary to reset the FD set between `select()` system calls?](https://stackoverflow.com/questions/4563577), [Are there any platforms where using structure copy on an FD set doesn't work?](https://stackoverflow.com/questions/2421672), [Why `select()` always returns 0 after the first timeout?](https://stackoverflow.com/questions/3324078), [Why is `select` used on Linux?](https://stackoverflow.com/questions/14544621) … – Jonathan Leffler Jun 15 '16 at 14:53
  • @JonathanLeffler Could you please elaborate a bit more about reconstruct the FD sets? does it mean I have to use `FD_COPY`? Thanks. – Peter D Jun 15 '16 at 14:56
  • @PeterD: I've added some links to semi-relevant questions which cover the issue. If FD_COPY() is available (it isn't standardized by POSIX), then you should be OK using it. See the question on using structure copy. – Jonathan Leffler Jun 15 '16 at 15:00
  • @JonathanLeffler I see. Thanks for the info. – Peter D Jun 15 '16 at 15:06

2 Answers2

1

You should have FD_ZERO and FD_SETs before calling select function.

E.g.

int res = 0;
for(;;){
     FD_ZERO(&read_fds);
     FD_ZERO(&write_fds);    

     fdmax = 0;
     size_t fd_index;
     for (fd_index=0; fd_index<num_of_fd; fd_index++)
     {
        FD_SET(fd_array[fd_index], &read_fds);  
        FD_SET(fd_array[fd_index], &write_fds);  

        if (fdmax < fd_array[fd_index])
           fdmax = fd_array[fd_index];
     }

    // write_fds = master;
    res = select(fdmax+1, &read_fds, &write_fds, NULL, NULL);

    if( res == 0 )
    {
       // TIMEOUT ...not your case
    }
    else if( res < 0 )
    {
        perror("Select error: ");
    }
    else
    {
        // process your data
    }

And you cannot write

read_fds = master;

because of fd_set is a complex struct.

EDIT

As @jeremyP commented sent < 0 should be sent > 0

LPs
  • 16,045
  • 8
  • 30
  • 61
0

In:

FD_SET(i, write_fds);

It should be:

FD_SET(i, &write_fds);

(note the ampersand).

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • Sorry, I missed that when trying to simplify the actual code to post it here. It is set as `FD_SET(i, &write_fds);` – Peter D Jun 15 '16 at 14:51