3

I'm new in C sockets programming, and I'm trying to implement client-server non-blocking application, by using select(...).

When I run on debug the server code and try to connect the client, the select(...) returns 1 (as expected) but for some reason, the FD_ISSET doesn't find the file descriptor ID in the descriptors set.

I've already spend several days in debugging and I'm running out of ideas what could be the problem. Any help would be much appreciated!

void server_listen(int port_number){


    tcpsock_t * server, * client;
    fd_set master_set, working_set;
    int listen_sd;

    int max_sd, rc, desc_ready, bytes, result, close_conn;
    dplist_node_t * reference;




    if (tcp_passive_open(&server,PORT)!=TCP_NO_ERROR) exit(EXIT_FAILURE);
    if (tcp_get_sd(server,&listen_sd)!=TCP_NO_ERROR) exit(EXIT_FAILURE);

    FD_ZERO(&master_set);
    max_sd = listen_sd;
    FD_SET(listen_sd, &master_set);

    do {
        timeout.tv_sec  = 30;
        timeout.tv_usec = 0;

        memcpy(&working_set, &master_set, sizeof(master_set));
        printf("Waiting on select()...\n");
        rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);

        if (rc < 0)
        {
            perror("  select() failed");
            break;
        }
        if (rc == 0)
        {
            printf("  select() timed out.\n");

        }
        desc_ready = rc;
        for (int i=1; i <= max_sd  &&  desc_ready > 0; ++i)
        {
            if (FD_ISSET(i, &working_set))
            {
                desc_ready -= 1;

                if (i == listen_sd)
                {
                    printf("  Listening socket is readable\n");

                  //do something with the new socket
                }

            }
            else
            {
                printf("  Descriptor %d is readable\n", i);
                close_conn = FALSE;

               //read data from already existing socket connection

            } /* End of existing connection is readable */
        } /* End of if (FD_ISSET(i, &working_set)) */
    }
    while (end_server == FALSE);

}

int tcp_passive_open(tcpsock_t ** sock, int port)
{
    int result;
    struct sockaddr_in addr;

    tcpsock_t * s = (tcpsock_t *) malloc(sizeof(tcpsock_t));
    s->cookie = 0;  
    s->port = -1;
    s->ip_addr = NULL;
    s->sd = -1;

    s->sd = socket(PROTOCOLFAMILY, TYPE, PROTOCOL);

    memset(&addr, 0, sizeof(struct sockaddr_in));
    addr.sin_family = PROTOCOLFAMILY;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(port);
    result = bind(s->sd,(struct sockaddr *)&addr,sizeof(addr));
    result = listen(s->sd,MAX_PENDING);
    s->ip_addr = NULL; 
    s->port = port;
    s->cookie = MAGIC_COOKIE;
    *sock = s;
    return TCP_NO_ERROR;
}

int tcp_get_sd(tcpsock_t * socket, int * sd)
{
  *sd = socket->sd;
  return TCP_NO_ERROR;
}
  • 2
    Q: Where are you re-initializing the fd_set (`working_set `)? It should be re-initialized before every `select` call. – Myst May 05 '16 at 17:42
  • 1
    I can't see any obvious flaws in the presented code. Have you checked whether `listen_sd` contains any useful value? And one comment: an `fd_set` is guaranteed to be a structure you can simply assign. The `memcpy` you use should work, but could be replaced with `working_set = master_set;` – tofro May 05 '16 at 17:48
  • @Myst she's using `memcpy`instead of an assignment. – tofro May 05 '16 at 17:49
  • related: http://stackoverflow.com/questions/3444729/using-accept-and-select-at-the-same-time – John Bollinger May 05 '16 at 18:09
  • Can you add a `printf("%d\n", listen_sd);` in there and tell us what it outputs? – Mark Plotnick May 05 '16 at 18:55
  • @MarkPlotnick, listen_sd=4 – Ksenia Beloturkina May 08 '16 at 21:20
  • In `tcp_passive_open()`, you don't appear to be checking the return values of your function calls for error codes. In particular, it looks like everything would fall down if the `socket()` call failed (returning `-1`). The overall behavior you describe is plausible for that case. – John Bollinger May 08 '16 at 21:35
  • On the other hand, it appears that you are indeed getting a positive file descriptor (indicating that `socket()` succeeded), but you do not verify that `bind()` and `listen()` in fact succeed. – John Bollinger May 08 '16 at 21:48
  • The `else` clause in the inner loop of `server_listen()` appears to be misplaced. It is associated with `if (FD_ISSET(...))`, but from its contents, it appears intended to be associated with `if (i == listen_sd)`. It does not appear that that explains the faulty behavior, however. – John Bollinger May 08 '16 at 21:55
  • You might obtain information relevant to the problem by running the server code under the `strace` utility. Most of the key calls here are system calls, and `strace` will report on the arguments and return values of all of those. – John Bollinger May 08 '16 at 21:59
  • 1
    Does the client successfully connect? The server does not need to `accept()` the connection for the client to see the connection attempt succeed. – John Bollinger May 08 '16 at 22:02

2 Answers2

1

Your for loop starts at 1. If listen_sd comes back as 0, your loop will fail.

So, change:

for (int i=1; i <= max_sd  &&  desc_ready > 0; ++i)

Into:

for (int i=0; i <= max_sd  &&  desc_ready > 0; ++i)

Also, with just one file descriptor, you don't really need the for loop at all.


UPDATE:

Since you've posted your tcp_* functions, I can comment further. Because you did not show what PROTOCOLFAMILY, TYPE, and PROTOCOL are, they are suspect.

Since you're having trouble, check the return values from socket, bind, and listen for any error.

For an example of a simple working client/server application, see my answer here: executing commands via sockets with popen()

Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • 5
    `listen_sd` is, according to the context (it is set by a function named `tcp_get_sd`), very *improbable* to be 0 - That would on most systems and in most conditions be the descriptor for `stdin`. But, agreed: Not *impossible* – tofro May 05 '16 at 18:27
  • 1
    @tofro If this is fired off as a server (e.g. from systemd), it probably has stdin closed. But, if the `select` worked (no timeout or fail--for which there are checks), the `FD_ISSET` _must_ succeed, so this is the _only_ possible explanation [as the `i <= max_sd` is correct]. I checked all the other stuff, too. If you check the code, it can't fail in any other way. – Craig Estey May 05 '16 at 18:33
  • @CraigEstey Nope, it would just have stdin set to `/dev/null`, not closed. – o11c May 05 '16 at 18:34
  • 1
    @o11c In my experience, it's always been closed. And, I just mentioned, there is _no_ other way for the code to fail if `select` neither times out nor errors out (i.e. the mask _must_ have at least one bit set) – Craig Estey May 05 '16 at 18:40
  • @CraigEstey: See my comment above: I was asking whether `listen_sd`was checked to contain any *reasonable* value. So we're on the same trail. But it could as well be -1 or whatever other value - Whereas you seem to be insisting on 0. – tofro May 05 '16 at 18:41
  • @tofro I presume the error check against `tcp_get_sd` return would preclude that (ie. why would it return success and return a negative fd?) – Craig Estey May 05 '16 at 18:44
  • @CraigEstey I don't know. Do you? Unfortunately, we're apparently not allowed to see that code which has already been mentioned in another comment. – tofro May 05 '16 at 18:47
  • @tofro Possible. Maybe OP will post the code for the `tcp_*` functions. Admittedly, I assumed they were from a [reliable] standard wrapper/helper library vs. [more] custom code. In lieu of posting it, OP could/should add a range check on `listen_sd` and also print it right after the `tcp_get_sd` – Craig Estey May 05 '16 at 19:03
0

I encounter the same problem "FD_ISSET returns always false" although I see the descriptor present in the descriptor set. The problem is follow rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout); should be rc = select(max_sd + 1, &set_for_read, &set_for_write, NULL, &timeout); You have to check both set if (FD_ISSET(i, &set_for_read))... if (FD_ISSET(i, &set_for_write))... As I see sometimes first condition is true and sometimes second. The codes:

  fd_set wrset;
  fd_set rdset;
  while (true) {
    rdset = writefds; 
    wrset = writefds;

    int selRet = select(0, &rdset, &wrset, NULL, NULL);

    for (SOCKET master_socket = 0; master_socket <= max_sd; master_socket++)
    {
        if (!FD_ISSET(master_socket, &rdset) && !FD_ISSET(master_socket, &wrset)) continue;
        if (master_socket == serverSoc) {
Sergey
  • 1
  • 1
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 26 '23 at 17:20