'select monitor two socket and return 1 instead of 2

server send "hello" after accept two client.

client recv "hello" and send something infintely.

Then, server uses select() waiting for data from two socket_fd.

I support that select() could detect if socket_fd had data in its buffer and result would be 2.

Acturally, it return 1 after some 2.

In wireshark, it caused a zero-window.

How can I fix it?

My simplfied code:

// server.cpp
#include <cstdio>
#include <cstring>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

struct sockaddr_in  ser_addr, cli_addr[2];
int                 lisn_fd, conn_fd[2];
fd_set              fs;
socklen_t           len;
char                buff[4096];

int main() {
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    ser_addr.sin_port = htons(9876);
    
    lisn_fd = socket(AF_INET, SOCK_STREAM, 0);
    bind(lisn_fd, (struct sockaddr*) &ser_addr, sizeof(ser_addr));
    listen(lisn_fd, 10);

    FD_ZERO(&fs);

    for (int i = 0; i < 2; i++) {
        conn_fd[i] = accept(lisn_fd, (struct sockaddr*) &cli_addr[i], &len);
        FD_SET(conn_fd[i], &fs);
    }
    int maxfd = conn_fd[0] > conn_fd[1]? conn_fd[0] : conn_fd[1];
    while (1) {
        write(conn_fd[0], "hello", 5);
        write(conn_fd[1], "hello", 5);
        int nready = select(maxfd+1, &fs, NULL, NULL, NULL);
        printf("nread = %d\n", nready);
        
        for (int i = 0; i < 2; i++) {
            if ( FD_ISSET(conn_fd[i], &fs) ) {
                printf("recv from client %d\n", i+1);
                ssize_t nread = read(conn_fd[i], buff, 4096);
                buff[nread] = 0;
                // puts(buff);
            }
        }
    }
    return 0;
}
// client.cpp

#include <arpa/inet.h>
#include <cstdio>
#include <cstring>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

struct sockaddr_in  ser_addr;
int                 conn_fd;


int main(){
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(9876);
    inet_pton(AF_INET, "127.0.0.1", &ser_addr.sin_addr);
    conn_fd = socket(AF_INET, SOCK_STREAM, 0);
    connect(conn_fd, (struct sockaddr*) &ser_addr, sizeof(ser_addr));
    
    char buff[4096];  
    buff[read(conn_fd, buff, 4096)] = 0;
    printf("recv: %s\n", buff);
    sprintf(buff, "hello from %d", getpid());
    while (1) {
        sleep(1);
        write(conn_fd, buff, strlen(buff));
    }

    return 0;
}
$ ./server
recv from client 1
recv from client 2
nread = 2
recv from client 1
recv from client 2
nread = 2
recv from client 1
recv from client 2
nread = 2
recv from client 1
recv from client 2
nread = 1
recv from client 1
nread = 1
recv from client 1
nread = 1
recv from client 1
nread = 1
recv from client 1
nread = 1
recv from client 1
^C
$ ./client
recv: hello
$ netstat -anp | rg server
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
Active Internet connections (servers and established)
tcp        0      0 0.0.0.0:9876            0.0.0.0:*               LISTEN      13805/./server      
tcp     2160      0 127.0.0.1:9876          127.0.0.1:54694         ESTABLISHED 13805/./server      
tcp        0      0 127.0.0.1:9876          127.0.0.1:54696         ESTABLISHED 13805/./server 

=================UPDATE===================

I don't expect sockets are alyways ready and the result is always 2.

The problem is if turn into 1, it will be 1 forever.

And I found a solution, but it is confusing.

// server.cpp
// ommit others
while (1) {
    // clear & reset fd_set before using select
    FD_ZERO(&fs);
    FD_SET(conn_fd[0], &fs);
    FD_SET(conn_fd[1], &fs);

I reset the fd_set, and the 1 and 2 will staggered appear.

Is select() will change somthing?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source