一.底层原理:本质是上委托内核去检测通信过程中文件描述符的一系列状态

二.select 函数详解:

首先我在Ubuntu上使用man查看一下帮助手册:

函数一共有五个参数:1.nfds:检测文件描述符集合中最大描述符+1;

2.readfds:读集合

3.writefds:写集合

4.exceptfds:异常集合

5.timeout:最长时间

函数解释:

  • fd_set 是一个文件描述符集合,用于 select 函数。
  • FD_ZERO 初始化集合,清空所有文件描述符。
  • FD_SET 将监听套接字 fd 添加到集合中。

服务器代码:

include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int main(int argc, const char* argv[]) {
    int lfd = socket(AF_INET, SOCK_STREAM, 0); // 创建监听套接字
    if (lfd == -1) {
        std::cout << "socket error" << std::endl;
        exit(0);
    }

    struct sockaddr_in ser_addr;
    memset(&ser_addr, 0, sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_port = htons(9999);
    ser_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    // 绑定端口
    int ret = bind(lfd, (struct sockaddr*)&ser_addr, sizeof(ser_addr));
    if (ret == -1) {
        std::cout << "bind error" << std::endl;
        exit(1);
    }

    // 监听
    ret = listen(lfd, 64);
    if (ret == -1) {
        std::cout << "listen error" << std::endl;
        exit(1);
    }

    fd_set redset;
    FD_ZERO(&redset);
    FD_SET(lfd, &redset);
    int maxfd = lfd;

    while (1) {
        fd_set tmp = redset;
        int ret = select(maxfd + 1, &tmp, NULL, NULL, NULL);

        // 处理监听套接字
        if (FD_ISSET(lfd, &tmp)) {
            int cfd = accept(lfd, NULL, NULL);
            if (cfd == -1) {
                std::cout << "accept error" << std::endl;
                exit(1);
            }
            FD_SET(cfd, &redset);
  if (cfd > maxfd) {
                maxfd = cfd;
            }
        }

        // 处理客户端套接字
        for (int i = 0; i <= maxfd; i++) {
            if (i != lfd && FD_ISSET(i, &tmp)) {
                char buf[1024];
                int len = recv(i, buf, sizeof(buf), 0);
                if (len == -1) {
                    std::cout << "recv error" << std::endl;
                    exit(1);
                } else if (len == 0) {
                    FD_CLR(i, &redset);
                    close(i);
                    std::cout << "client is closed" << std::endl;
                    break;
                }

                // 转换为大写
                for (int j = 0; j < len; j++) {
                    buf[j] = toupper(buf[j]);
                    std::cout << "alter buf: " << buf[j] << std::endl;
                }

                ret = send(i, buf, strlen(buf) + 1, 0);
                if (ret == -1) {
                    std::cout << "send error" << std::endl;
                }
            }
        }
    }

    close(lfd);
    return 0;
}
运行结果如下:


由于没有客户端连接,程序一直阻塞,现在我们找一个博主最近写的项目(后续做完也会在本网站上开源和发布)

博主在这里设置当连接成功后程序才能跑起来,显然博主写的大概没啥问题。今天的文章就更新到这了。