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

如何使用select同时处理多个Socket连接?(C语言)

如何用C语言的select函数同时处理多个Socket连接

嘿,我来给你一步步拆解怎么用C语言的select实现多Socket连接的同时读取,结合你给出的代码片段来详细讲~

先搞懂select的核心逻辑

select是IO多路复用的经典工具,它能让我们同时监控一堆文件描述符(这里就是Socket)的状态变化,不用给每个连接单独开线程或进程,能大大节省系统资源。简单说就是:告诉内核“帮我盯着这些Socket,哪个有数据可读或者有新连接了就告诉我”。

完整代码+逐段讲解

先把你给出的代码补全成可运行的版本,再一步步解释:

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int port = 3008;
int listenfd;

// 创建监听Socket的函数
void makelistener() {
    struct sockaddr_in servaddr;
    // 创建Socket
    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    if (listenfd == -1) {
        perror("socket create failed");
        exit(1);
    }
    // 设置端口复用,避免程序重启后端口被占用
    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    // 绑定地址和端口
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 监听所有网卡
    servaddr.sin_port = htons(port);
    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
        perror("bind failed");
        exit(1);
    }
    // 开始监听,最大等待队列设为10
    if (listen(listenfd, 10) == -1) {
        perror("listen failed");
        exit(1);
    }
    printf("Listening on port %d...\n", port);
}

int main(int argc, char **argv) {
    makelistener();
    int clientfd, nready;
    socklen_t len;
    struct sockaddr_in q;
    int i;
    char buf[1024];
    int maxfd;
    fd_set allset, rset; // allset存所有要监控的fd,rset是每次select的临时集合

    // 1. 初始化文件描述符集合
    FD_ZERO(&allset);          // 先清空集合
    FD_SET(listenfd, &allset); // 把监听Socket加入集合
    maxfd = listenfd;          // 记录当前最大的fd,select需要这个值

    while (1) {
        // 2. 每次调用select前,复制allset到rset(因为select会修改传入的集合)
        rset = allset;
        // 调用select,监听读事件(第二个参数是读集合),阻塞直到有事件发生
        nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
        if (nready == -1) {
            perror("select failed");
            exit(1);
        }

        // 3. 处理新的客户端连接
        if (FD_ISSET(listenfd, &rset)) {
            // 接受新连接
            len = sizeof(q);
            clientfd = accept(listenfd, (struct sockaddr *)&q, &len);
            if (clientfd == -1) {
                perror("accept failed");
                continue;
            }
            printf("New client connected: %s:%d\n", inet_ntoa(q.sin_addr), ntohs(q.sin_port));
            // 把新的clientfd加入监控集合
            FD_SET(clientfd, &allset);
            // 更新maxfd,如果新的fd比当前大的话
            if (clientfd > maxfd) {
                maxfd = clientfd;
            }
            // 处理完一个事件,nready减1,减到0就不用再检查后面的fd了
            if (--nready == 0) {
                continue;
            }
        }

        // 4. 处理已连接客户端的读事件
        for (i = listenfd + 1; i <= maxfd; i++) {
            if (FD_ISSET(i, &rset)) {
                // 读取客户端发送的数据
                int n = read(i, buf, sizeof(buf));
                if (n <= 0) {
                    // n=0说明客户端断开连接,n=-1是读错误
                    printf("Client disconnected: fd=%d\n", i);
                    close(i);
                    FD_CLR(i, &allset); // 从集合里移除这个fd
                } else {
                    // 这里简单处理:把小写转大写再发回去
                    for (int j = 0; j < n; j++) {
                        buf[j] = toupper(buf[j]);
                    }
                    write(i, buf, n);
                    printf("Received and echoed %d bytes from fd=%d\n", n, i);
                }
                if (--nready == 0) {
                    break; // 所有事件都处理完了,跳出循环
                }
            }
        }
    }
    return 0;
}

关键细节提醒

  • 必须初始化集合:每次使用fd_set前一定要用FD_ZERO清空,不然会有垃圾数据导致异常。
  • 每次select要重置集合:select会把传入的集合修改为只有就绪的fd,所以每次调用前都要从allset复制一份到临时集合rset。
  • 跟踪最大fd:select的第一个参数是maxfd + 1,内核会从0遍历到这个值,所以跟踪最大fd能提高效率。
  • 及时清理断开的连接:客户端断开后要调用close()关闭fd,并用FD_CLR从集合里移除,避免无效fd占用资源。

内容的提问来源于stack exchange,提问作者e t

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:26:31