cnetworkingbufferposix-select

C: Using a select call, when I am reading, how do I keep track of the data?


First of all, I've never worked with C before (mostly Java which is the reason you'll find me write some naive C code). I am writing a simple command interpreter in C. I have something like this:

//Initialization code

if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
    perror("Select dead");
    exit(EXIT_FAILURE);
}

....
....
//Loop through connections to see who has the data ready
//If the data is ready
if ((nbytes = recv(i, buf, sizeof(buf), 0)) > 0) {
     //Do something with the message in the buffer
}

Now if I'm looking at something like a long paragraph of commands, it is obvious that a 256 byte buffer will not be able to get the entire command. For the time being, I'm using a 2056 byte buffer to get the entire command. But if I want to use the 256 byte buffer, how would I go about doing this? Do I keep track of which client gave me what data and append it to some buffer? I mean, use something like two dimensional arrays and such?


Solution

  • Yes, the usual approach is to have a buffer of "data I've received but not processed" for each client, large enough to hold the biggest protocol message.

    You read into that buffer (always keeping track of how much data is currently in the buffer), and after each read, check to see if you have a complete message (or message(s), since you might get two at once!). If you do, you process the message, remove it from the buffer and shift any remaining data up to the start of the buffer.

    Something roughly along the lines of:

    for (i = 0; i < nclients; i++)
    {
        if (!FD_ISSET(client[i].fd, &read_fds))
            continue;
    
        nbytes = recv(client[i].fd, client[i].buf + client[i].bytes, sizeof(client[i].buf) - client[i].bytes, 0);
    
        if (nbytes > 0)
        {
            client[i].bytes += nbytes;
    
            while (check_for_message(client[i]))
            {
                size_t message_len;
    
                message_len = process_message(client[i]);
                client[i].bytes -= message_len;
                memmove(client[i].buf, client[i].buf + message_len, client[i].bytes);
            }
        }
        else
            /* Handle client close or error */
    }
    

    By the way, you should check for errno == EINTR if select() returns -1, and just loop around again - that's not a fatal error.