multithreading - TCP clients not receiving messages in multi-threaded C application - Stack Overflow

admin2025-04-29  1

I am learning multi-threading in C and I'm trying to build a simple chat application. The idea is that clients connect to a central server and the server forwards messages sent by the clients to the other clients, just like a group chat.

I have almost everything done except for the reception of messages of from a client. My TCP server receives the messages just fine, passes the appropriate parameters like the clients Socket File Descriptor and received buffers to the corresponding functions that re-send everything. But the client just does not receive anything. I do not know what to do.

Here are some relevant functions of my application, but you can also see the current stage of my application in this repository.

/* sock_server.c */
#include "sock_util.h"


#define SRV_ADDR ""
#define SRV_PORT 2357
#define LST_BACKLOG 2

int main() {

    int server_sfd;
    if ((server_sfd = runTCPServer(SRV_ADDR, SRV_PORT, LST_BACKLOG)) < 0) {
        return -1;
    }

    runThreadConnections(server_sfd);

    shutdown(server_sfd, SHUT_RDWR);
    LOG_INFO_MESSAGE("Application closed.\n");  

    return 0;
}
/* sock_client.c */
#include "sock_util.h"

#define SRV_ADDR "127.0.0.1"
#define SRV_PORT 2357

#define NEWCONN_CMD "//newconn\n"
#define ENDCONN_CMD "//endconn\n"


void *threadMessageReceiver(void *sfd) {

    char buffer[1024];
    int client_sfd = *(int *)sfd;
    ssize_t char_count;

    free(sfd);

    LOG_INFO_MESSAGE("Receiving messages. [SFD %d].\n", client_sfd);

    while (true) {
        if ((char_count = recv(client_sfd, buffer, strlen(buffer), 0)) < 0) {
            printf("Error.\n");
            return NULL;
        }

        if (char_count == 0) {
            printf("Received nothing.\n");
            continue;
        }

        buffer[char_count] = 0;  // Add NULL character to end the string.
        LOG_INFO_MESSAGE("Message received: [ %s ]\n", buffer);
    }

    LOG_INFO_MESSAGE("No longer receiving messages.\n");
    return 0;
}


int runThreadMessageReceiver(int *client_sfd) {
    runInThread(threadMessageReceiver, client_sfd, sizeof(client_sfd));
    return 0;
}


int main() {
        
    int sfd = createTCPv4Socket();  
    struct sockaddr_in *addr = createIPv4Sockaddr(SRV_ADDR, SRV_PORT);  

    // connect to the address using the socket.
    if (connect(sfd, (struct sockaddr *) addr, sizeof(*addr)) == -1) {
        printf("- something went wrong: %s", strerror(errno));
        return -1; 
    }

    // receive messages assycronously
    // runThreadMessageReceiver(&sfd);
    
    // beginning of chat application
    char *line = NULL;
    size_t line_s = 0;  
    ssize_t char_count;

    while (true) {  
        if ((char_count = getline(&line, &line_s, stdin)) < 0) {
            return -1;
        }
        fflush(stdout);

        // finish conversation.
        if (strlen(line) == 1) {
            send(sfd, ENDCONN_CMD, strlen(ENDCONN_CMD), 0);
            break;
        };  
        
        
        if (send(sfd, line, char_count, 0) == -1) {
            return -1;
        }

    }   
    
    close(sfd);
    return 0;
}

Relevant functions in sock_util.c

int runInThread(void *(*routine)(void *), void *routine_arg, size_t arg_size) {
    /*
     Helper function to run any function with any argument in a sepate thread.
     The function will return -1 on error or the thread id on success.
    */

    // Allocate memory for the thread argument
    void *arg_copy = malloc(arg_size);
    if (!arg_copy) {
        LOG_ERROR_MESSAGE("Error with malloc(): %s.\n", strerror(errno));
        return -1;
    }

    // Copy the content of the argument into the allocated memory
    memcpy(arg_copy, routine_arg, arg_size);

    pthread_t id;
    if(pthread_create(&id, NULL, routine, arg_copy) < 0) {
        LOG_ERROR_MESSAGE("Error with pthread(): %s.\n", strerror(errno));
        return -1;
    }
    LOG_DEBUG_MESSAGE("Thread created succesfully: ID %02x.\n", id);

    return id;
}

int runThreadConnections(int sfd_srv) { 
    /*
     Start running each client connection in a separate thread.
     Client messages are handled within a thread in each client's 
     corresponding thread.
    */

    while (true) {
        struct acceptedConn *client_conn = acceptNewConn(sfd_srv);
        if (client_conn->error < 0) {
            printf("- Error with AcceptNewConn(): %s.\n", strerror(errno));
            return -1;
        }

        LOG_DEBUG_MESSAGE("Client connection accepted: SocketFD %d.\n", client_conn->sfd_client);

        int sfd_client = client_conn->sfd_client;
        int *sfd_client_ptr = &sfd_client;

        connections_list[connections_count++] = *client_conn; 
        LOG_DEBUG_MESSAGE("Connections list updated.\n");

        // Run threadNewConn() from a new thread pthread_create()
        LOG_DEBUG_MESSAGE("Calling runInThead() from runThreadConnections().\n");
        runInThread(threadConnections, sfd_client_ptr, sizeof(sfd_client));
    }
}

void *threadConnections(void *arg_sfd_client) {
    int sfd_client = *(int *)arg_sfd_client; 
    free(arg_sfd_client);

    listenConn(sfd_client); 
    close(sfd_client);

    return NULL;
}

int listenConn(int sfd_client) {
    /*
     Process a connection by listening to a received accepted connection from `acceptedConn()` using
     the `listen()^ function.
     Loop indifinitely until the connection is closed by the client or an error arise.
    */

    char buff_recv[1024];
    ssize_t recv_content = 0;
    while (true) {
        //BUG Verify the recv_content errno case and when recv_content = 1 || 0.

        if ( (recv_content = recv(sfd_client, buff_recv, 1024, 0)) < 0) {
            if (errno == 104) {  // if connection reset exit gracefully
                LOG_DEBUG_MESSAGE("Client disconnected. ClientFD %d.\n", sfd_client);
                break;
            }
            printf("- error with recv(): %s.\n", strerror(errno));
            return -1;
        }

        if(recv_content == 1 || recv_content == 0) {
            break;
        }

        buff_recv[recv_content - 1] = 0;  // We assume all received messages end with a breakline \n.

        LOG_DEBUG_MESSAGE("Client [SocketFD %d] message received: ", sfd_client);
        printf("[ %s ]\n", buff_recv);

        //TODO: Check for messages starting with "//" (Commands)
        if (strstr(buff_recv, "//") == NULL) {
            runThreadReply(buff_recv, sfd_client, 0);
        }
    }

    return 0;
} 

int runThreadReply(char* buffer, int sender_sfd, int receiver_sfd) {
    /*
     Send the sender_sfd received buffer to receiver_sfd.
     if receiver_sfd is 0, the buffer is sent to every client connected with the server.
    */
    LOG_DEBUG_MESSAGE("Calling runInThead() from runThreadReplies().\n");

    for (int i = 0; i < connections_count; i++) {
        int sfd_client = connections_list[i].sfd_client;

        // if (sfd_client == sender_sfd) continue;

        LOG_DEBUG_MESSAGE("Buffer from %d to %d.\n", sender_sfd, sfd_client);

        struct recv_info *info = malloc(sizeof(struct recv_info));
        info->recv_sfd = sfd_client;
        info->buffer = buffer;
        info->buffer_size = strlen(buffer);
        info->flags = 0;

        send(sfd_client, buffer, strlen(buffer), 0);

        if (sfd_client == receiver_sfd || receiver_sfd == 0) {
            // runInThread(threadReply, info, sizeof(struct recv_info));    
        }
    }
    
    return 0;
}

void *threadReply(void *recv_info) {
    struct recv_info info = *(struct recv_info *)recv_info;
    
    if (strlen(info.buffer) <= 0) {
        send(info.recv_sfd, info.buffer, strlen(info.buffer), info.flags);
    }

    LOG_DEBUG_MESSAGE("Message %s sended to %d.\n", info.buffer, info.recv_sfd);

    return NULL;
}

I tried to log everything step by step but I cannot find the mistake in my logic. Since I do not know multithreading well, it may be some race condition or some management failure in my threaded application, or just a silly mistake. I do not know.

I am learning multi-threading in C and I'm trying to build a simple chat application. The idea is that clients connect to a central server and the server forwards messages sent by the clients to the other clients, just like a group chat.

I have almost everything done except for the reception of messages of from a client. My TCP server receives the messages just fine, passes the appropriate parameters like the clients Socket File Descriptor and received buffers to the corresponding functions that re-send everything. But the client just does not receive anything. I do not know what to do.

Here are some relevant functions of my application, but you can also see the current stage of my application in this repository.

/* sock_server.c */
#include "sock_util.h"


#define SRV_ADDR ""
#define SRV_PORT 2357
#define LST_BACKLOG 2

int main() {

    int server_sfd;
    if ((server_sfd = runTCPServer(SRV_ADDR, SRV_PORT, LST_BACKLOG)) < 0) {
        return -1;
    }

    runThreadConnections(server_sfd);

    shutdown(server_sfd, SHUT_RDWR);
    LOG_INFO_MESSAGE("Application closed.\n");  

    return 0;
}
/* sock_client.c */
#include "sock_util.h"

#define SRV_ADDR "127.0.0.1"
#define SRV_PORT 2357

#define NEWCONN_CMD "//newconn\n"
#define ENDCONN_CMD "//endconn\n"


void *threadMessageReceiver(void *sfd) {

    char buffer[1024];
    int client_sfd = *(int *)sfd;
    ssize_t char_count;

    free(sfd);

    LOG_INFO_MESSAGE("Receiving messages. [SFD %d].\n", client_sfd);

    while (true) {
        if ((char_count = recv(client_sfd, buffer, strlen(buffer), 0)) < 0) {
            printf("Error.\n");
            return NULL;
        }

        if (char_count == 0) {
            printf("Received nothing.\n");
            continue;
        }

        buffer[char_count] = 0;  // Add NULL character to end the string.
        LOG_INFO_MESSAGE("Message received: [ %s ]\n", buffer);
    }

    LOG_INFO_MESSAGE("No longer receiving messages.\n");
    return 0;
}


int runThreadMessageReceiver(int *client_sfd) {
    runInThread(threadMessageReceiver, client_sfd, sizeof(client_sfd));
    return 0;
}


int main() {
        
    int sfd = createTCPv4Socket();  
    struct sockaddr_in *addr = createIPv4Sockaddr(SRV_ADDR, SRV_PORT);  

    // connect to the address using the socket.
    if (connect(sfd, (struct sockaddr *) addr, sizeof(*addr)) == -1) {
        printf("- something went wrong: %s", strerror(errno));
        return -1; 
    }

    // receive messages assycronously
    // runThreadMessageReceiver(&sfd);
    
    // beginning of chat application
    char *line = NULL;
    size_t line_s = 0;  
    ssize_t char_count;

    while (true) {  
        if ((char_count = getline(&line, &line_s, stdin)) < 0) {
            return -1;
        }
        fflush(stdout);

        // finish conversation.
        if (strlen(line) == 1) {
            send(sfd, ENDCONN_CMD, strlen(ENDCONN_CMD), 0);
            break;
        };  
        
        
        if (send(sfd, line, char_count, 0) == -1) {
            return -1;
        }

    }   
    
    close(sfd);
    return 0;
}

Relevant functions in sock_util.c

int runInThread(void *(*routine)(void *), void *routine_arg, size_t arg_size) {
    /*
     Helper function to run any function with any argument in a sepate thread.
     The function will return -1 on error or the thread id on success.
    */

    // Allocate memory for the thread argument
    void *arg_copy = malloc(arg_size);
    if (!arg_copy) {
        LOG_ERROR_MESSAGE("Error with malloc(): %s.\n", strerror(errno));
        return -1;
    }

    // Copy the content of the argument into the allocated memory
    memcpy(arg_copy, routine_arg, arg_size);

    pthread_t id;
    if(pthread_create(&id, NULL, routine, arg_copy) < 0) {
        LOG_ERROR_MESSAGE("Error with pthread(): %s.\n", strerror(errno));
        return -1;
    }
    LOG_DEBUG_MESSAGE("Thread created succesfully: ID %02x.\n", id);

    return id;
}

int runThreadConnections(int sfd_srv) { 
    /*
     Start running each client connection in a separate thread.
     Client messages are handled within a thread in each client's 
     corresponding thread.
    */

    while (true) {
        struct acceptedConn *client_conn = acceptNewConn(sfd_srv);
        if (client_conn->error < 0) {
            printf("- Error with AcceptNewConn(): %s.\n", strerror(errno));
            return -1;
        }

        LOG_DEBUG_MESSAGE("Client connection accepted: SocketFD %d.\n", client_conn->sfd_client);

        int sfd_client = client_conn->sfd_client;
        int *sfd_client_ptr = &sfd_client;

        connections_list[connections_count++] = *client_conn; 
        LOG_DEBUG_MESSAGE("Connections list updated.\n");

        // Run threadNewConn() from a new thread pthread_create()
        LOG_DEBUG_MESSAGE("Calling runInThead() from runThreadConnections().\n");
        runInThread(threadConnections, sfd_client_ptr, sizeof(sfd_client));
    }
}

void *threadConnections(void *arg_sfd_client) {
    int sfd_client = *(int *)arg_sfd_client; 
    free(arg_sfd_client);

    listenConn(sfd_client); 
    close(sfd_client);

    return NULL;
}

int listenConn(int sfd_client) {
    /*
     Process a connection by listening to a received accepted connection from `acceptedConn()` using
     the `listen()^ function.
     Loop indifinitely until the connection is closed by the client or an error arise.
    */

    char buff_recv[1024];
    ssize_t recv_content = 0;
    while (true) {
        //BUG Verify the recv_content errno case and when recv_content = 1 || 0.

        if ( (recv_content = recv(sfd_client, buff_recv, 1024, 0)) < 0) {
            if (errno == 104) {  // if connection reset exit gracefully
                LOG_DEBUG_MESSAGE("Client disconnected. ClientFD %d.\n", sfd_client);
                break;
            }
            printf("- error with recv(): %s.\n", strerror(errno));
            return -1;
        }

        if(recv_content == 1 || recv_content == 0) {
            break;
        }

        buff_recv[recv_content - 1] = 0;  // We assume all received messages end with a breakline \n.

        LOG_DEBUG_MESSAGE("Client [SocketFD %d] message received: ", sfd_client);
        printf("[ %s ]\n", buff_recv);

        //TODO: Check for messages starting with "//" (Commands)
        if (strstr(buff_recv, "//") == NULL) {
            runThreadReply(buff_recv, sfd_client, 0);
        }
    }

    return 0;
} 

int runThreadReply(char* buffer, int sender_sfd, int receiver_sfd) {
    /*
     Send the sender_sfd received buffer to receiver_sfd.
     if receiver_sfd is 0, the buffer is sent to every client connected with the server.
    */
    LOG_DEBUG_MESSAGE("Calling runInThead() from runThreadReplies().\n");

    for (int i = 0; i < connections_count; i++) {
        int sfd_client = connections_list[i].sfd_client;

        // if (sfd_client == sender_sfd) continue;

        LOG_DEBUG_MESSAGE("Buffer from %d to %d.\n", sender_sfd, sfd_client);

        struct recv_info *info = malloc(sizeof(struct recv_info));
        info->recv_sfd = sfd_client;
        info->buffer = buffer;
        info->buffer_size = strlen(buffer);
        info->flags = 0;

        send(sfd_client, buffer, strlen(buffer), 0);

        if (sfd_client == receiver_sfd || receiver_sfd == 0) {
            // runInThread(threadReply, info, sizeof(struct recv_info));    
        }
    }
    
    return 0;
}

void *threadReply(void *recv_info) {
    struct recv_info info = *(struct recv_info *)recv_info;
    
    if (strlen(info.buffer) <= 0) {
        send(info.recv_sfd, info.buffer, strlen(info.buffer), info.flags);
    }

    LOG_DEBUG_MESSAGE("Message %s sended to %d.\n", info.buffer, info.recv_sfd);

    return NULL;
}

I tried to log everything step by step but I cannot find the mistake in my logic. Since I do not know multithreading well, it may be some race condition or some management failure in my threaded application, or just a silly mistake. I do not know.

Share Improve this question asked Jan 6 at 22:43 Az JRCAz JRC 356 bronze badges 5
  • 1 Note that if recv() returns 0, that means the TCP connection has been closed... i.e. you will never receive any more data on that TCP connection. So responding to that by doing a continue of your event-loop will just lead to the event loop spinning the CPU and not doing anything useful. You're better off doing something like printf("Connection closed!\n"); break; – Jeremy Friesner Commented Jan 6 at 23:06
  • 1 Also you don't seem to be checking the return values of your calls to send(). So if they are erroring out, you wouldn't know why or where. Always check return values, otherwise you are just making your own life harder by hiding the ball from yourself :) – Jeremy Friesner Commented Jan 6 at 23:09
  • 1 // We assume all received messages end with a breakline \n. You shouldn't assume this. When using a TCP connection, you can't assume that each call to recv() will correspond to a single send() call. – Barmar Commented Jan 6 at 23:10
  • 1 Can you get it working if you don't use multi-threading? Try to narrow this down to a socket problem or threading. – Barmar Commented Jan 6 at 23:12
  • Questions seeking debugging help must generally provide a minimal reproducible example of the problem inside the question itself. Posting a link to an external code repository is not sufficient. – Andreas Wenzel Commented Jan 7 at 6:21
Add a comment  | 

1 Answer 1

Reset to default 3

The reason is the third parameter of the recv call in function threadMessageReceiver. It should be the buffer length, but you used strlen(buffer), which is always 0. That's why your client keeps printing Received nothing.

void *threadMessageReceiver(void *sfd) {
    ...
    char buffer[1024];
    int client_sfd = *(int *)sfd;
    ssize_t char_count;

    free(sfd);
    while (true) {
        if ((char_count = recv(client_sfd, buffer, strlen(buffer), 0)) < 0) { // BUG !!!
     ...
    }
    return 0;
}

You should use sizeof(buf)-1(minus one for string ending) instead.

void *threadMessageReceiver(void *sfd) {
    ...
    char buffer[1024];
    int client_sfd = *(int *)sfd;
    ssize_t char_count;

    free(sfd);
    while (true) {
        if ((char_count = recv(client_sfd, buffer, sizeof(buffer)-1, 0)) < 0) {
     ...
    }
    return 0;
}
转载请注明原文地址:http://anycun.com/QandA/1745940709a91416.html