fd_set的具体实现过程

fd_set的实现详细原理


define FD_SETSIZE      1024
typedef unsigned long   fd_mask;
#define NBBY    8               /* number of bits in a byte */
#define NFDBITS (sizeof(fd_mask) * NBBY)        /* bits per mask */
#define howmany(x, y)   (((x) + ((y) - 1)) / (y))
typedef struct fd_set {
        fd_mask fds_bits[howmany(FD_SETSIZE, NFDBITS)];
} fd_set;
#define _fdset_mask(n)  ((fd_mask)1 << ((n) % NFDBITS))
#define FD_SET(n, p)    ((p)->fds_bits[(n)/NFDBITS] |= _fdset_mask(n))
#define FD_CLR(n, p)    ((p)->fds_bits[(n)/NFDBITS] &= ~_fdset_mask(n))
#define FD_ISSET(n, p)  ((p)->fds_bits[(n)/NFDBITS] & _fdset_mask(n))
#define FD_COPY(f, t)   bcopy(f, t, sizeof(*(f)))
#define FD_ZERO(p)      bzero(p, sizeof(*(p)))

假如有如下定义:

fd_set read_set;

FD_SET(600,read_set);

那么最终的推导如下:
=> read_set->fds_bits[(600 + 32 - 1)/32] |= (1<<(600%32));

### FD_SET 的定义与用法 #### 定义 `FD_SET` 是一种数据结构,用于表示一组文件描述符(file descriptors),通常在套接字编程中被用来管理多个 I/O 流的状态。它本质上是一个位图或者数组,能够容纳一定数量的文件描述符[^2]。 在 Windows 平台下,`fd_set` 结构体的具体实现如下: ```c typedef struct fd_set { u_int fd_count; /* 当前设置的数量 */ SOCKET fd_array[FD_SETSIZE]; /* 文件描述符数组 */ } fd_set; ``` 其中 `fd_count` 表示当前集合中有多少个有效的文件描述符,而 `fd_array` 则是用来存储这些文件描述符的一个固定大小的数组,默认情况下其最大容量为 64 (`FD_SETSIZE`)。 #### 函数说明 以下是几个常用的宏操作函数及其功能: 1. **`FD_ZERO(fd_set *set)`** 初始化一个 `fd_set` 集合,将其清零,即将所有的成员标记为未选中状态[^1]。 2. **`FD_SET(int fd, fd_set *set)`** 将指定的文件描述符加入到给定的 `fd_set` 中。如果该文件描述符已经存在于集合中,则不会重复添加[^2]。 3. **`FD_CLR(int fd, fd_set *set)`** 移除某个特定的文件描述符从 `fd_set` 集合里删除^。 4. **`FD_ISSET(int fd, const fd_set *set)`** 检测某文件描述符是否属于此集合的一部分;如果是则返回非零值,否则返回零[^1]. #### 使用场景 当需要监控多个网络连接或本地资源时,可以通过组合使用 `select()` 和上述四个宏命令来高效地完成任务。例如,在服务器端程序设计过程中经常需要用到这种方法监听客户端请求到来的情况[^3]: - 创建并初始化若干个 `fd_set` 对象分别代表读取、写入以及异常事件发生可能性的不同类别; - 调整好各个方向上的最高编号加一作为参数传递给 `select`; - 执行完毕之后再逐一判断哪些句柄处于就绪态以便进一步处理逻辑分支走向. --- ### 示例代码展示如何利用 `FD_SET` 下面给出了一段简单的 C++ 实现例子展示了怎样运用这些工具构建一个多路复用I/O模型: ```cpp #include <iostream> #include <winsock2.h> #pragma comment(lib,"ws2_32.lib") int main(){ WSADATA wsaData; WSAStartup(MAKEWORD(2,2),&wsaData); SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); sockaddr_in serverAddr {}; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8080); serverAddr.sin_addr.s_addr = INADDR_ANY; bind(listenSocket,(struct sockaddr*)&serverAddr,sizeof(serverAddr)); listen(listenSocket,SOMAXCONN); fd_set masterSet, workingSet; FD_ZERO(&masterSet); FD_SET(listenSocket,&masterSet); while(true){ memcpy(&workingSet,&masterSet,sizeof(masterSet)); if(select(0,&workingSet,NULL,NULL,NULL)>0){ for(auto i=0;i<FD_SETSIZE;++i){ if(FD_ISSET(i,&workingSet)){ if(i==listenSocket){ auto clientSock = accept(listenSocket,nullptr,nullptr); std::cout << "New connection accepted."<<std::endl; FD_SET(clientSock,&masterSet); } else{ char buffer[1024]; int bytesRecv = recv(i,buffer,sizeof(buffer)-1,0); if(bytesRecv>0){ send(i,buffer,strlen(buffer)+1,0); } else{ closesocket(i); FD_CLR(i,&masterSet); } } } } } } WSACleanup(); } ``` 上面这段代码创建了一个基本的服务端应用程序框架,它可以接受来自不同客户的并发通信尝试,并且回显收到的消息内容回去给他们知道收到了什么信息过来啦! ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值