【问题标题】:Can an application handle N accept connections and each accept uses an independent thread一个应用程序是否可以处理 N 个接受连接并且每个接受使用一个独立的线程
【发布时间】:2013-11-24 15:11:59
【问题描述】:

我有 C linux TCP 客户端/服务器应用程序。 我想出了一个奇怪的场景,但我不知道这个应用程序是否有任何后果。 我有一个可以接受 N 个连接的服务器端,例如这个服务器将接受 100 个连接。 在这种情况下,我在主线程中创建监听套接字,然后创建 100 个线程,每个线程有一个独立的 accept() 和一个 select() iomux,每个线程也只能接受一个连接。

我在这里担心的是,如果两个同时接受()因为选择准备在同一个套接字上读取而想要接受同一个套接字(连接),我不知道同时接受在内核中是否是线程安全的并且只有一个接受可以处理该传入连接,而另一个将等待另一个连接?

我在我的 RedHat 机器上进行了尝试,它运行良好,但我不知道我是否有幸避免了死锁!

谢谢

rc = bind(sd, (struct sockaddr_in *)& groupSock, sizeof(struct sockaddr_in));
    CHECK_VALUE("Bind address error", rc, 0, goto cleanup);

    rc = listen(sd, 10);
    CHECK_VALUE("listen", rc, 0, goto cleanup);

    for(; count< num_socks; count++){

            par_data[count].sd = sd;
            par_data[count].thread_num = count;
            par_data[count].err_chk = -1;

            rc = pthread_create(&thread_id[count], NULL, accept_sock_thread,  (void *)& par_data[count]);
            CHECK_VALUE("pthread_create", rc, 0, goto cleanup);


    }

void * accept_sock_thread(void* atr){

    int                     rc;
    int                     sock            = INVALID_SOCKET;
    int                     datalen         = config.traffic;
    char                    *databuf        = NULL;
    struct thread_data      *data           = NULL;
    struct sockaddr_in      tcp_remote;
    struct timeval          t;
    socklen_t               size;
    fd_set                  socks;

    databuf = malloc(sizeof(char) * datalen);
    memset(databuf, 0, datalen);

    data = (struct thread_data*) atr;
    DEBUG(my_debug_flags, ENTER_FUNCT, ("Enter Function accept_sock_thread thread_num %d \n", data->thread_num));


    FD_ZERO(&socks);
    FD_SET(data->sd, &socks);
    t.tv_sec = 25;
    t.tv_usec = 0;
    rc = select(data->sd + 1 , &socks, NULL, NULL,&t);
    if(rc < 0){
            VL_MISC_ERR(("Error in select with Errno: %d", errno));
            goto cleanup;
    }
    else if(rc == 0){
            VL_MISC_ERR(("Accept Select returned a TIEMOUT."));
            goto cleanup;
    }


    size = sizeof(struct sockaddr_in);
    sock = accept(data->sd, (struct sockaddr *)& tcp_remote, &size);
    CHECK_NOT_EQUAL("tcp accept error", sock, INVALID_SOCKET,  goto cleanup);
cleanup:
    //      sleep(2); /* avoid EOF */
    if(sock != INVALID_SOCKET){

            rc = close(sock);
            if(rc != 0){
                    data->err_chk = -1;
            }
    }
           return NULL;
}

【问题讨论】:

  • 你能贴出处理连接的代码吗?您描述的场景可能很好,但也取决于实现
  • sd = socket(AF_INET, SOCK_STREAM, 0); rc = bind(sd, (struct sockaddr_in *)& groupSock, sizeof(struct sockaddr_in)); rc = 听(SD,100); for(;count
  • 其实明白了,看我的回答。

标签: c linux sockets linux-kernel posix


【解决方案1】:

accept() 根据 POSIX 是线程安全和可重入的。

这意味着对同一个描述符的两次接受调用不应给出未定义的行为。一个接受会打开套接字,另一个会返回错误。

你可以在那里看到更多:

Is accept() thread-safe? Are BSD/Posix sockets reentrant?

【讨论】:

【解决方案2】:

只有一个线程会去accept 连接。内核确保了这一点。很长一段时间以来,unix/posix 世界都是这样的。

【讨论】:

  • 内核确保这一点。除非内核承诺补偿任何不真实的进程?
【解决方案3】:

只有一个线程会accept(),到目前为止一切顺利...... - 但在此之前所有线程将被触发以返回select(),这可能不是您想要的。

因此,如果您有 N 个线程在 select() 中休眠,并且 一个 连接进入 所有 线程都会唤醒,但只有 一个 > 将是必需的,因为只有 一个 会在accept()ing 中成功。

【讨论】:

  • 是的,所有选择都将返回准备阅读,然后幸运的接受将获得连接...谢谢
  • @GEEKS:从性能的角度来看,我认为这非常低效。另请参阅 Richard Chambers 在他上面的评论中链接的问题的答案。
【解决方案4】:

在来自多个线程/进程的同一个套接字对象上使用accept() 不仅是标准的,而且是广泛使用的策略。据我所知,apache 就是这样做的。 nginx 也这样做,但方式略有不同。

注意:select() 可以唤醒多个线程,但只有一个线程会接受连接,而其他线程要么a)挂在accept() 要么b) 在非阻塞 IO 的情况下返回 -1 并将 errno 设置为 EAGAIN。由于您在 accept() 之前使用 select() 我想套接字描述符 is 处于非阻塞模式。所以这会导致一些线程从不提供连接。

另外我建议你不要在多个线程中关闭同一个套接字。这可能会导致非常讨厌且难以调试的后果。要么使用包装器和shared_ptr,要么为其中一个线程分配“套接字所有者”的角色。

【讨论】:

    猜你喜欢
    • 2011-07-17
    • 1970-01-01
    • 2023-02-11
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    相关资源
    最近更新 更多