RTC222
RTC222

Reputation: 2323

UNIX domain datagram socket blocks on recvfrom() with select()

I am using UNIX domain datagram sockets to send records from multiple clients to a single server in a multithreaded program. Everything is done within one process; I'm sending records from multiple threads to a single thread that acts as the server. All threads are assigned to separate cores using their affinity masks.

My problem is when I use select() to retrieve records from client sockets that have records in the socket buffer. I am using the same basic setup I used with a single client socket (and it worked in that context), but now it hangs (apparently it blocks) when I call recvfrom. That's surprising because the select() function has already identified the socket as available for reading.

int select_clientsockets(int64_t srvrfd, int64_t * claddr, int fds_array[], int fd_count, void * recvbuf){

    int fds_ready;
    int abc;
    int64_t cli_addr;

    FD_ZERO(&fdset);
    FD_SET(0,&fdset);

    socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);

    fds_ready = select(3, &fdset, NULL, NULL, 0);

    for (int i = 0; i < fd_count; i++){

        fds_array[i] = 0;

        if (FD_ISSET(i, &fdset)) {
            fds_array[i] = 1;
            cli_addr = claddr[i];
            server_receive(srvrfd, recvbuf, 720, cli_addr);}
    }

    return 0;
}

The select function calls server_receive on clients where select says data are available:

int64_t server_receive(int64_t sfd, void * buf, int64_t msgLen, int64_t claddr)
{
    socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);
    int numBytes = recvfrom(sfd, buf, BUF_SIZE, 0, (struct sockaddr *) claddr, len);

    if (numBytes == -1)
        return 0;

    return numBytes;
}

The client socket address is taken from the 3-element array "claddr" (for 3 client sockets) where the corresponding position for each client socket is filled in when the socket is created. At socket creation I also call FD_SET to set the client address into the fd_set. I think I should get the client socket address from fd_set instead, BUT they're both the same pointer value so I don't know why that would make a difference. For internet domain datagram sockets we can use getpeername() but I don't know if there is an analogous function for UNIX domain sockets -- or even if that's the problem.

Thanks very much for any help with this.

UPDATE:

Client fds are added to the global fdset struct on socket creation:

int64_t * create_socket_client(struct sockaddr_un claddr, int64_t retvals[])
{
    int sfd, j;
    size_t msgLen;
    ssize_t numBytes;
    char resp[BUF_SIZE];

    retvals[0] = 0;
    retvals[1] = 0;

    sfd = socket(AF_UNIX, SOCK_DGRAM, 0);
    if (sfd == -1)
        return retvals;

    memset(&claddr, 0, sizeof(struct sockaddr_un));
    claddr.sun_family = AF_UNIX;
    snprintf(claddr.sun_path, sizeof(claddr.sun_path),  "/tmp/ud_ucase_cl.%ld", (long) getpid());

    FD_SET(sfd,&fdset);

    retvals[0] = sfd;
    retvals[1] = (int64_t)&claddr;

     return retvals;
}

Upvotes: 0

Views: 394

Answers (1)

David Schwartz
David Schwartz

Reputation: 182761

FD_ZERO(&fdset);
FD_SET(0,&fdset);

socklen_t * len = (socklen_t * ) sizeof(struct sockaddr_un);

fds_ready = select(3, &fdset, NULL, NULL, 0);

for (int i = 0; i < fd_count; i++){
    fds_array[i] = 0;

    if (FD_ISSET(i, &fdset)) {

Your code empties fdset then adds only 0 to fdset. So when you call select and pass it fdset, you are asking it only to check socket 0 for readiness.

You later check if sockets 0 to one less than fd_count are in fdset, but only zero could possibly be because it's the only one you asked about.

Where is the list of sockets you want to check for readiness?

Upvotes: 2

Related Questions