c - 这个C多线程tcp服务器正确吗?

标签 c multithreading sockets semaphore tcpserver

我创建了一个多线程 C TCP 服务器。它似乎可以工作(作为客户端,我输入一条消息,该消息被发送到服务器,服务器打印客户端在线程中发送的内容(并发回客户端 ID)。

我是否尊重 C 多线程 TCP 服务器的“最佳实践”? 也许我应该使用信号量来访问/使用 client_counter 变量?

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h>

#include <unistd.h> // disable close() warning
#include <sys/socket.h> 
#include <sys/types.h> 
#include <netinet/in.h> 

#include <pthread.h>

#define MAX_CONNECTIONS 5

static int client_counter = 0;

void* serverWorker(void* context)
{
    char client_response[256];

    int sock = *(int*)context;
    char message[256]  = "\n Hello dear client, you are the client number \n";
    char numero[12];
    sprintf(numero, "%d", client_counter); // SHOULD I USE A SEMAPHORE HERE FOR client_counter ?

    while(1)
    {
        memset(client_response, 0, sizeof(client_response)); // clean string
        recv(sock, &client_response, sizeof(client_response), 0);
        printf("client number %s sent: '%s' \n", numero, client_response);
        if (send(sock, numero , strlen(numero) , 0) < 0)
        {
            printf("ERROR while sending response to client from worker \n");
        }
    }
    return NULL;
}



int main() 
{ 

    printf("Waiting for incoming connections ...\n");

    // socket creation 
    int server_socket;
    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    // dserver address
    struct sockaddr_in server_address;
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(9002);
    server_address.sin_addr.s_addr = INADDR_ANY;

    // bind the socket to IP and port
    bind(server_socket, (struct sockaddr*) &server_address, sizeof(server_address));

    listen(server_socket, MAX_CONNECTIONS);

    int client_socket;
    while((client_socket = accept(server_socket, NULL ,NULL)))
    {
        client_counter++; 
        pthread_t thread_id;
        pthread_create(&thread_id, NULL, serverWorker, (void*)&client_socket);
        printf("new client ! \n");
    }

    close(server_socket);

    return 0; 
} 

最佳答案

您的代码中有几个问题...您在传入连接上创建一个线程,并将所有创建的线程传递一个对存储套接字描述符的变量的引用(相同的引用)。这将使所有线程共享同一个变量来存储从通配符获得的所有套接字描述符。也许您认为好吧,我只是在线程启动时进行复制,所以这不会发生,但是想想几乎同时进入的两个连接,线程 main() 运行并处理这两个连接。然后,第一个和第二个线程被调度,并且两者都获得存储的相同描述符(第二个),并且第一个连接被泄漏。

另一件事是,虽然这个变量是 main 的本地变量,但一旦 main() 返回(这不是程序的结束,如果线程要在 main()return 之后存活下来),它就会停止存在,但是当你处于无限循环中(您可能不知道,但 server_socket 给出错误的唯一方法是,如果您在线程中销毁( close() 它),或者删除它所附加的接口(interface)。)这可能会导致到 SIGSEGV 陷阱。

您可以自由地将 int 值转换为 (void *),而不会出现任何问题,因为线程体函数会在使用前将其转换回 int,这会减少为 noop,因为指针类型的大小通常更大(或等于,但不小于 int 。无论如何,这完全是未定义的行为,但可能会起作用(因为遗留软件充满了此类转换,因此所有编译器通常都会实现以尝试尊重这一点)正确的方法是声明struct 的信息在启动时传递给线程并从线程返回。然后您可以在其中存储任何您想要的内容,但是请考虑一下,由于您将有动态数量的线程,因此您需要动态分配结构。

关于 client_counter 变量的使用,唯一接触该变量的线程是运行 main() 代码的线程。这不会带来比上面提到的风险更大的问题,快速顺序的两次更新可以使两个线程在 main 进行两次更新后都获得 main 中更新的值。

另一个问题是,您需要将其声明为 volatile,因为线程代码不会假设它仅在访问之间被更改,并且可能会将其缓存为寄存器变量。

main() 和您获取的不同线程之间传递的消息可以通过两种方式实现。这就是例程在输入时获取 void * 并在返回时返回 void * 的原因:

  • 第一个使用动态 struct 本地数据( malloc() ed,从 main() 传递到线程,并在终止时返回(当您将线程加入到 main 时)。这种方式允许您从main 中的线程,然后您必须 free(3) main 中的结构。该结构用作线程和主例程之间双向的通信消息,您可以在其中存储需要传递或返回的任何信息。线程完成后,您可以对 main 中的结构进行 free() (不要在线程中执行此操作,因为它必须在死亡后继续存在)

  • 第二个涉及不再与 main() 进行通信,并且一旦完成,线程必须释放该结构。这更简单,也更适合您的示例。通过这种方式,您可以在线程或 main 中销毁该结构,但前提是您已经加入该线程并且确定该结构不会被它使用。

关于c - 这个C多线程tcp服务器正确吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53444600/

相关文章:

c - 如何在 vi/vim 中查找 C 代码中函数的调用者和被调用者?

c - 这个缓冲区是如何工作的?

c - 组合字符串后打印时未获得任何输出

Java:让 ExecutorService 产生可重复的行为?

c - 在 BST 中查找 "nth"值 (C)

java - 在eclipse中调试多个线程

c# - CPU使用率在执行过程中变得非常高

java - 在 Android 和 PC 之间创建 Wifi 点对点连接

c - recvfrom 调用时出现段错误

javascript - 从服务器端将属性绑定(bind)到套接字对象似乎无法正常工作