21xrx.com
2024-12-27 11:10:30 Friday
登录
文章检索 我的文章 写文章
C++中的select函数的使用
2023-07-05 07:06:19 深夜i     --     --
C++ select函数 使用

在C++编程中,select函数是一个非常有用的函数。它允许程序员检测多个文件描述符的状态,以便在任一文件描述符准备好可读、可写或出错时通知程序员。这个函数通常被用来监控套接字,因此在客户端-服务器应用程序和网络编程中非常常用。

在C++中,select函数有如下语法:


int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

其中,参数nfds是最后一个文件描述符加1的值(int fdmax = nfds - 1),readfds是一个指向fd_set结构的指针,表示读取状态,writefds是一个指向fd_set结构的指针,表示写入状态,exceptfds同理也是一个指向fd_set结构的指针,表示异常状态,timeout指针指向一个timeval结构,表示阻塞等待的最长时间,如果timeout是NULL,则select函数将一直等待,直到有数据可以读。

fd_set结构体被用于保存文件描述符集合。当在调用select()时,读取fd_set结构体中的文件描述符,如果其中1个文件描述符可以读取(即已经准备好),则select函数返回1,否则select函数返回0。

一个基本的使用例子如下:


#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <sys/socket.h>

#include <arpa/inet.h>

#define PORT 8080

int main() {

  int server_fd, new_socket, valread;

  struct sockaddr_in address;

  int opt = 1;

  int addrlen = sizeof(address);

  char buffer[1024] = {0};

  fd_set readfds;

  // 创建socket文件描述符

  if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {

    perror("socket failed");

    exit(EXIT_FAILURE);

  }

  // 绑定socket到指定IP和端口

  if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt))) {

    perror("setsockopt");

    exit(EXIT_FAILURE);

  }

  address.sin_family = AF_INET;

  address.sin_addr.s_addr = INADDR_ANY;

  address.sin_port = htons(PORT);

  if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {

    perror("bind failed");

    exit(EXIT_FAILURE);

  }

  // 监听文件描述符上的连接请求

  if (listen(server_fd, 3) < 0) {

    perror("listen");

    exit(EXIT_FAILURE);

  }

  printf("Listening on port %d...\n", PORT);

  // 清除fd_set数据集合

  FD_ZERO(&readfds);

  // 将服务端socket文件描述符添加到fd_set数据集合中

  FD_SET(server_fd, &readfds);

  while (true) {

    int max_fd = server_fd;

    // 遍历fd_set中的文件描述符并进行处理

    for (int i = 0; i <= max_fd; i++) {

      if (FD_ISSET(i, &readfds)) {

        // 如果是服务器文件描述符,则创建新套接字

        if (i == server_fd) {

          if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {

            perror("accept");

            exit(EXIT_FAILURE);

          }

          printf("New connection...\n");

          // 将新套接字加入fd_set的数据集合中

          FD_SET(new_socket, &readfds);

          if (new_socket > max_fd)

            max_fd = new_socket;

          

        } else {

          // 否则从客户端接收数据

          valread = read(i, buffer, 1024);

          if (valread == 0) {

            // 如果客户端关闭连接,则从fd_set中删除该套接字

            close(i);

            printf("Disconnected socket %d.\n", i);

            FD_CLR(i, &readfds);

          } else {

            printf("Received data: %s", buffer);

          }

        }

      }

    }

  }

  return 0;

}

在上述代码中,我们创建了一个简单的TCP服务器,可以接受来自客户端的连接,并从客户端接收数据。我们使用select()来同时处理多个文件描述符的状态。在主函数中的while循环中,我们使用FD_ZERO(&readfds)来初始化fd_set结构体,并使用FD_SET()将服务器的socket文件描述符添加到fd_set中。

接下来,在遍历fd_set时,我们使用FD_ISSET()检查当前文件描述符是否处于可读取状态(即是否可以接受数据)。如果是服务器socket文件描述符,则通过accept()创建一个新套接字,并将其添加到fd_set中。否则,我们从客户端读取数据并在控制台中打印出来。

如果客户端关闭了连接,则我们从fd_set集合中删除该套接字,并使用FD_CLR()函数。最后,我们使用FD_SET()将新的套接字添加到fd_set结构体中。

总之,select函数是一个非常有用的工具,在多个文件描述符的情况下能够简化程序开发,并且能够监控文件描述符集合的状态,有助于开发高效的客户端-服务器应用程序和网络编程。

  
  

评论区

{{item['qq_nickname']}}
()
回复
回复