Search code examples
csocketsposix-select

Recv interfering with recvfrom inside same select statement in C


I'm trying to create a select server that can handle both TCP and UDP conenctions, but when I try to recieve a UDP message, the prescence of my recv stops the code from reaching the recvfrom. I can still reciveve the UDP message if I send it twice (not sure why). How might I change this to correctly recieve from tcpfd on recv, and udpfd on recvfrom (those are the only two read fd's that will ever be in the read master set)?
The UDP message is being sent from an identical server on the same computer (and is sent correctly), and both sockets I'm using have been correctly set up. The recvfrom works correctly if I remove the recv if statement. Code is based on https://www.gnu.org/software/libc/manual/html_node/Server-Example.html#Server-Example

while (1)
{
    //masterset is contains 'tcpfd' and 'udpfd' for the tcp and udp socket I want to listen to
    readset = master;  

    if (select(FD_SETSIZE, &readset, NULL, NULL, NULL) >= 0)
    {
        for (int i = 0; i <= FD_SETSIZE; i++)
        {
            len = sizeof servaddr;

            if (FD_ISSET(i, &readset))
            {

                if (i == tcpfd)
                {
                    int new;
                    new = accept(tcpfd, (struct sockaddr *)&servaddr, &len);
                    if (new < 0)
                    {
                        perror("accept");
                        exit(EXIT_FAILURE);
                    }
                    else
                    {
                        FD_SET(new, &masterset);
                    }
                }

                else if ((i != udpfd) && ((nbytes = recv(i, buffer, sizeof(buffer), 0)) > 0))
                {

                }

                else if ((i == udpfd) && (nbytes = recvfrom(udpfd, buffer, sizeof(buffer), 0, 
                (struct sockaddr *)&servaddr, &len)) > 0)
                {

                }
            }
        }
    }

Solution

  • There are multiple problems with your code, but the one most likely causing the problem is that select modifies the sets you pass to it.

    That means if readset have two descriptors sets in it but only one is readable, then the second will be removed from the set.

    You need to reset the sets you pass to select before the next call in the loop. I assume that's what masterset is supposed to be used for, but you forgot to actually use it.


    As for the other problems, the most serious is the loop condition i <= FD_SETSIZE. Descriptor sets are really arrays, and your loop will lead to an off-by-one error and out-of-bounds indexing into the array. This of course leads to undefined behavior.