Pieter
Pieter

Reputation: 32805

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?

Upvotes: 10

Views: 10318

Answers (4)

fizzer
fizzer

Reputation: 13806

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;
}

Upvotes: 12

dgraves
dgraves

Reputation: 351

You could use select() to wait for incoming data and avoid blocking in recv(). select() will also block, but you can have it time out after a set interval so that the while loop can continue and check for signals to quit from the main thread:

while (main thread not calling for receiver to quit) {
  if (tcpCon.hasData(500)) {              // Relies on select() to determine that data is
                                          // available; times out after 500 milliseconds
    string message = tcpCon.tcpReceive(); // Relies on the recv() function
    processIncomingMessage(message);
  }
}

Upvotes: 5

stijn
stijn

Reputation: 35911

calling close on the socket from any other thread will make the recv call fail instantly.

Upvotes: 2

GazTheDestroyer
GazTheDestroyer

Reputation: 21261

If you close the socket in another thread, then recv() will exit.

Upvotes: 2

Related Questions