Open JDK 在 1.6 版本以后就取消了 NIO 在 Linux 下的 select 模型,只提供 epoll 模型,那么这里我们就模拟一下 NIO 在 Linux 下调用 select 模型
Linux 下的 select 函数
NAMEselect, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexingSYNOPSIS/* According to POSIX.1-2001, POSIX.1-2008 */#include <sys/select.h>/* According to earlier standards */#include <sys/time.h>#include <sys/types.h>#include <unistd.h>int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);void FD_CLR(int fd, fd_set *set);int FD_ISSET(int fd, fd_set *set);void FD_SET(int fd, fd_set *set);void FD_ZERO(fd_set *set);#include <sys/select.h>int pselect(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, const struct timespec *timeout,const sigset_t *sigmask);
- nfds:ndfs 应该设置为三个集合中编号最高的文件描述符加1。表示集合中的每个文件描述符都会被检查,直到达到这个限制
- readfds:监听需要读的文件描述符集合
- writefds:监听需要写的文件描述符集合
- exceptfds:监听异常事件的文件描述符集合
- timeout:监听超时时间
select 代码
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>void start() {// 创建服务端结构体struct sockaddr_in my_addr;my_addr.sin_family = AF_INET;my_addr.sin_port = htons(8080);my_addr.sin_addr.s_addr = htonl(INADDR_ANY);// 创建客户端结构体struct sockaddr_in client_addr;char client_ip[INET_ADDRSTRLEN] = "";int clientfd = 0;// 创建 socketint listenfd = socket(AF_INET, SOCK_STREAM, 0);bind(listenfd, (struct sockaddr*)&my_addr, sizeof(my_addr));listen(listenfd, 128);printf("listen client @port=%d...\n", 8080);int lastfd = listenfd;int i;// 创建读事件集合fd_set read_set, total_set;FD_ZERO(&read_set);FD_SET(listenfd, &total_set); // 将 listenfd 加入读事件集合while(1) {read_set = total_set;// 返回三种事件的集合// select 会阻塞监听int z = select(lastfd + 1, &read_set, NULL, NULL, NULL);if (z > 0) {// 判断 listenfd 是否在 read_set 集合中if (FD_ISSET(listenfd, &read_set)) {socklen_t client_len = sizeof(client_addr);clientfd = accept(listenfd, (struct sockaddr*)&client_addr, &client_len);// 打印连接信息inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);printf("----------------------------------\n");printf("client ip=%s, port=%d\n", client_ip, ntohs(client_addr.sin_port));// 将新的 clientfd 加入集合FD_SET(clientfd, &total_set);lastfd = clientfd;// 只有一个连接事件的时候,不在往下执行if (0 == --z) {continue;}}for (i = listenfd + 1; i <= lastfd; i++) {// 判断 i 是否在 read_set 集合中,也就是 i 发送了信息,发生了读事件if (FD_ISSET(i, &read_set)) {char recv_buf[1024] = "";int rs = read(i, recv_buf, sizeof(recv_buf));// 0 表示对方关闭了连接if (rs == 0) {// 关闭 iclose(i);// 将 i 从 total_set 中移除FD_CLR(i, &total_set);} else {printf("%s\n", recv_buf);// write(0, recv_buf, rs);}}}}}}int main() {start();return 0;}
编译
gcc select.c -o select.out
执行
./select.outlisten client @port=8080...
测试
nc 127.0.0.1 8080test
服务端显示:
listen client @port=8080...----------------------------------client ip=127.0.0.1, port=6742test
