select
是 Linux 系统中的一个 I/O 多路复用函数,它允许单个进程/线程处理多个 I/O 操作。它主要用于网络编程,以实现高效的服务器和客户端通信。
select
函数监视多个文件描述符(例如套接字),等待其中任意一个变为可读、可写或有异常条件待处理。
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
nfds
: 文件描述符集合中最大描述符加 1。readfds
: 监听可读事件的文件描述符集合。writefds
: 监听可写事件的文件描述符集合。exceptfds
: 监听异常事件的文件描述符集合。timeout
: 设置超时时间。select
可以实现非阻塞的读写操作。以下是一个简单的 select
使用示例,创建一个监听套接字并接受客户端连接:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/select.h>
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
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);
}
// 绑定和监听
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(8080);
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);
}
while (1) {
// 清空文件描述符集合
FD_ZERO(&readfds);
// 添加监听套接字到集合
FD_SET(server_fd, &readfds);
// 设置超时时间
struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
// 使用 select 监听
int activity = select(server_fd + 1, &readfds, NULL, NULL, &timeout);
if ((activity < 0) && (errno != EINTR)) {
perror("select error");
}
// 检查是否有新的连接
if (FD_ISSET(server_fd, &readfds)) {
if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
perror("accept");
exit(EXIT_FAILURE);
}
printf("New connection, socket fd is %d, ip is : %s, port : %d\n", new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
// 处理客户端请求...
}
}
return 0;
}
accept
后正确关闭不再需要的文件描述符。timeout
参数,避免无限等待。select
返回值进行检查,并适当处理各种错误情况。通过以上方法,可以有效利用 select
提升程序的并发处理能力。