TCP 客户端在多线程 C 应用程序中未接收消息

问题描述 投票:0回答:1

我正在学习 C 语言的多线程,我正在尝试构建一个简单的聊天应用程序。这个想法是客户端连接到中央服务器,服务器将客户端发送的消息转发给其他客户端,就像群聊一样。

除了接收客户的消息之外,我几乎完成了所有事情。我的 TCP 服务器很好地接收消息,将适当的参数(例如客户端套接字文件描述符)和接收到的缓冲区传递给重新发送所有内容的相应函数。但客户只是没有收到任何东西。我不知道该怎么办。

这里是我的应用程序的一些相关功能,但您也可以在这个存储库中看到我的应用程序的当前阶段。

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

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

我尝试一步步记录所有内容,但我找不到逻辑中的错误。由于我不太了解多线程,这可能是我的线程应用程序中的一些竞争条件或一些管理失败,或者只是一个愚蠢的错误。我不知道。

c multithreading pthreads send recv
1个回答
0
投票

原因是

recv
调用的第三个参数。它应该是缓冲区长度,但你使用了
strlen(buffer)
,它始终是
0
。这就是您的客户不断打印的原因
Received nothing.

© www.soinside.com 2019 - 2024. All rights reserved.