fd_set记录

#define __FD_SETSIZE 1024
#define __NFDBITS (8 * sizeof(unsigned long))
#define __FDSET_LONGS (__FD_SETSIZE/__NFDBITS)
typedef struct {
	unsigned long fds_bits [__FDSET_LONGS];
} __kernel_fd_set;
typedef __kernel_fd_set   fd_set;


1.将对应内存全部置为0

#define FD_ZERO(fdsetp) __FD_ZERO(fdsetp)
#define __FD_ZERO(fdsetp)   (memset (fdsetp, 0, sizeof (*(fd_set *)(fdsetp))))
2.映射fd到数组的某一个位上面

#define FD_SET(fd,fdsetp) __FD_SET(fd,fdsetp)
#define __FD_SET(fd, fdsetp)   (((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] |= (1<<((fd) & 31)))

3.通过句柄fd检测某一位是否已经置了值

#define FD_ISSET(fd,fdsetp) __FD_ISSET(fd,fdsetp)
#define __FD_ISSET(fd, fdsetp)   ((((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] & (1<<((fd) & 31))) != 0)
4.根据fd将此前SET过的值清除掉
#define FD_CLR(fd,fdsetp) __FD_CLR(fd,fdsetp)
#define __FD_CLR(fd, fdsetp)   (((fd_set *)(fdsetp))->fds_bits[(fd) >> 5] &= ~(1<<((fd) & 31)))


### Select 函数的参数和用法 `select` 是一种用于监控文件描述符集合状态变化的 I/O 多路复用机制,在 C 编程中广泛应用于网络编程和其他异步事件处理场景。以下是 `select` 的主要功能及其参数说明: #### 原型定义 ```c #include <sys/select.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ``` #### 参数详解 1. **nfds**: 表示需要监视的最大文件描述符值加一(即最大文件描述符编号 + 1)。此参数决定了 `select` 需要扫描的范围[^5]。 2. **readfds**: 这是一个指向 `fd_set` 类型变量的指针,表示需要监听可读性的文件描述符集合。如果某个文件描述符变为可读,则该集合会被修改以反映这一变化[^5]。 3. **writefds**: 同样是指向 `fd_set` 类型变量的指针,表示需要监听可写性的文件描述符集合。当某些文件描述符变为可写时,对应的位会被设置为有效状态[^5]。 4. **exceptfds**: 此参数指定了一组文件描述符,这些描述符上的异常条件会触发通知。通常情况下,这个参数较少使用,但在特定场合下可能非常重要[^5]。 5. **timeout**: 定义了 `select` 调用等待的时间上限。它可以通过传递一个 `struct timeval` 结构体来实现超时控制;如果将其设为 NULL,则意味着无限期阻塞直到有活动发生[^5]。 #### 使用方法 为了有效地利用 `select` 函数,程序员需遵循以下流程: - 初始化并清空所有的 `fd_set` 变量。 - 将感兴趣的文件描述符加入到相应的集合并更新 `nfds` 值。 - 设置合适的超时期限或者禁用超时。 - 调用 `select` 并依据返回的结果分析哪些文件描述符发生了改变。 下面展示了一个简单的例子,演示如何通过 `select` 来管理多个套接字连接的状态检测过程。 ```c #include <stdio.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/time.h> #include <sys/types.h> #include <sys/select.h> #define MAX_CLIENTS 10 #define BUFFER_SIZE 1024 int main() { int server_fd, new_socket; struct sockaddr_in address; int addrlen = sizeof(address); // 创建 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); bind(server_fd, (struct sockaddr *)&address, sizeof(address)); listen(server_fd, 3); fd_set readfds; while (1) { FD_ZERO(&readfds); // 清除集合中的所有成员 FD_SET(server_fd, &readfds); // 添加服务器监听套接字至集合 int max_sd = server_fd; // 记录当前最大的文件描述符号 // 检查客户端列表是否有数据待接收 for (int i = 0; i < MAX_CLIENTS; ++i){ int sd = client_sockets[i]; if(sd > 0) FD_SET(sd , &readfds); // 如果存在有效的客户端则添加 if(sd > max_sd) max_sd = sd; // 更新最大文件描述符数值 } struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; int activity = select(max_sd + 1, &readfds, NULL, NULL, &tv); if((activity < 0) && (errno != EINTR)){ printf("select error\n"); } // 若发现新的连接请求... if(FD_ISSET(server_fd, &readfds)){ if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0){ perror("accept"); continue; } printf("New connection accepted.\n"); // 加入新建立的连接到数组里以便后续跟踪其行为模式 for(int j=0;j<MAX_CLIENTS;++j){ if(client_sockets[j]==0){ client_sockets[j]=new_socket; break; } } } // 对于每一个已存在的客户机进行轮询操作看是否存在任何消息传入情况 for(int k=0;k<MAX_CLIENTS;k++){ int sock_desc = client_sockets[k]; if(FD_ISSET(sock_desc,&readfds)){ char buffer[BUFFER_SIZE]={0}; int valread=read(sock_desc,buffer,BUFFER_SIZE); if(valread==0){ getpeername(sock_desc,(struct sockaddr*)&address, (socklen_t*)&addrlen); printf("Host disconnected: %s:%d \n",inet_ntoa(address.sin_addr), ntohs(address.sin_port)); close(sock_desc); client_sockets[k]=0; }else{ printf("Client sent message: %s\n",buffer ); send(sock_desc,"Message received.",strlen("Message received."),MSG_NOSIGNAL); } } } } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值