You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++多客户端WebSocket应用Linux下select()无法检测客户端数据问题

问题根因及修复方案

核心问题点

  • 异步设置fd_set存在时序/线程安全问题:你在FD_ZERO后启动异步线程setting_file_thread向fr集合添加客户端套接字,但select调用和异步线程执行没有同步机制,Linux下调度逻辑和Windows不同,大概率select执行时异步线程还没完成客户端fd的添加,导致fr集合里只有服务端监听套接字,自然只能检测到连接请求。
  • 局部std::future的隐式阻塞问题:fu_1是if块内的局部变量,块结束时future自动析构,对于std::launch::async启动的任务,future析构会阻塞等待任务执行完成,就算你等到任务完成,如果max_fd没有更新,超过max_fd的客户端fd也不会被select处理。
  • max_fd未正确维护:你每次新增客户端套接字到队列后,没有更新max_fd为当前所有监听套接字(服务端+所有客户端)中的最大值,select的第一个参数是监听的最大fd+1,超过这个值的fd即使加到fd_set里也不会被检测。
  • fd_set操作线程不安全:如果fr是类成员变量,多线程同时操作fd_set结构会导致内部数据错乱,即使加了客户端fd也无法被正确识别。
  • 读事件未遍历处理:select返回后你只判断了服务端套接字的事件,else分支没有遍历所有客户端套接字判断哪个触发了读事件,就算有事件也无法正确对应处理。

修复建议

  • 去掉异步设置fd_set的逻辑,直接在主线程中遍历客户端队列,把所有客户端fd逐一FD_SET到fr集合中,同时维护更新max_fd的值。
  • select返回后如果不是服务端的连接事件,遍历所有客户端套接字,调用FD_ISSET判断哪个有读事件,再针对对应fd做接收处理。
  • 不要在事件检测分支直接开异步处理读写,避免大量连接时线程溢出,要么用线程池,要么直接在当前线程处理非阻塞读写。

修复后核心代码示例

#include <sys/ioctl.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/select.h>
void Server::server_connection_handle (){
    // 清空文件描述符集合
    FD_ZERO(&fr);
    FD_SET(server_sock, &fr);
    max_fd = server_sock; // 初始化max_fd为服务端监听套接字的值

    // 主线程直接添加所有客户端fd,避免异步时序问题
    for (int cli_fd : queue) {
        FD_SET(cli_fd, &fr);
        if (cli_fd > max_fd) {
            max_fd = cli_fd; // 同步更新最大fd值
        }
    }
                  
    if (select(max_fd + 1, &fr,NULL, NULL, NULL) > 0){
        if (FD_ISSET(server_sock, &fr)){
            int addr_len = sizeof(sockaddr);
            // 接收新连接
            int cli_sock = accept(server_sock , (struct sockaddr *)&addr, (socklen_t *)&addr_len);
            sock_listen->test_connection(cli_sock);
            // 新客户端加入队列
            queue.emplace_back(cli_sock);
            std::cout << "收到来自套接字("<< cli_sock <<")的连接请求,即将处理" << std::endl;
        }else {
            // 遍历所有客户端查找触发读事件的套接字
            for (auto it = queue.begin(); it != queue.end(); ) {
                int cli_fd = *it;
                if (FD_ISSET(cli_fd, &fr)) {
                    // 替换为你自身的接收逻辑
                    ssize_t recv_len = recv(cli_fd, 接收缓冲区, 缓冲区大小, 0);
                    if (recv_len <= 0) {
                        // 连接异常/断开,关闭套接字并移除队列
                        close(cli_fd);
                        it = queue.erase(it);
                        continue;
                    }
                    // 处理接收到的数据逻辑
                }
                ++it;
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者Ahmed Ezzeldin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.05 12:12:03