Pieter Pieter - 3 months ago 7
C Question

Stopping a receiver thread that blocks on recv()

I have a chat application that has a separate thread to listen for incoming messages.

while (main thread not calling for receiver to quit) {
string message = tcpCon.tcpReceive(); // Relies on the recv() function
processIncomingMessage(message);
}


This way of working has one big problem. Most of the time, the loop will be blocking on
recv()
so the receiver thread won't quit. What would be a proper way to tackle this issue without forcing thread termination after a couple of seconds?

Answer

Close the socket with shutdown() to close it for all receivers.

This prints out 'recv returned 0' on my system, indicating that the receiver saw an orderly shutdown. Comment out shutdown() and watch it hang forever.

Longer term, OP should fix the design, either using select or including an explicit quit message in the protocol.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

/* Free on my system. YMMV */
int port = 7777;
int cd;

void *f(void *arg)
{
    /* Hack: proper code would synchronize here */
    sleep(1);

    /* This works: */
    shutdown(cd, SHUT_RDWR);

    close(cd);
    return 0;
}

int main(void)
{
    /* Create a fake server which sends nothing */
    int sd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in sa = { 0 };
    const int on = 1;
    char buf;
    pthread_t thread;
    sa.sin_family = AF_INET;
    sa.sin_addr.s_addr = htonl(INADDR_ANY);
    sa.sin_port = htons(port);
    setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);

    /* Other error reporting omitted for clarity */
    if (bind(sd, (const struct sockaddr*)&sa, sizeof sa) < 0) {
        perror("bind");
        return EXIT_FAILURE;
    }

    /* Create a client */
    listen(sd, 1);
    cd = socket(AF_INET, SOCK_STREAM, 0);
    connect(cd, (const struct sockaddr*)&sa, sizeof sa);
    accept(sd, 0, 0);

    /* Try to close socket on another thread */
    pthread_create(&thread, 0, f, 0);
    printf("recv returned %d\n", recv(cd, &buf, 1, 0));
    pthread_join(thread, 0);

    return 0;
}