select函数说明
select函数原型:
int select(int nfds, fd_set *rdfds, fd_set *wtfds, fd_set *exfds, struct timeval *timeout)
select函数有两点限制:
- 进程能够打开的最大文件描述符个数
- select函数中,fd_set集合所能够容纳的最大文件描述符个数
函数实例代码
#include <unistd.h> #include <sys/types.h> /* basic system data types */ #include <sys/socket.h> /* basic socket definitions */ #include <netinet/in.h> /* sockaddr_in{} and other Internet defns */ #include <arpa/inet.h> /* inet(3) functions */ #include <sys/select.h> /* select function*/ #include <stdlib.h> #include <errno.h> #include <stdio.h> #include <string.h> #define MAXLINE 10240 void handle(int * clientSockFds, int maxFds, fd_set* pRset, fd_set* pAllset); int main(int argc, char **argv) { int servPort = 6888; // 服务器端口 int listenq = 1024; int listenfd, connfd; // 监听套接字,已连接套接字 struct sockaddr_in cliaddr, servaddr; // 定义服务器,客户端地址结构体 socklen_t socklen = sizeof(struct sockaddr_in); int nready, nread; char buf[MAXLINE]; int clientSockFds[FD_SETSIZE]; fd_set allset, rset; int maxfd; listenfd = socket(AF_INET, SOCK_STREAM, 0); // 创建监听套接字 if (listenfd < 0) { perror("socket error"); return -1; } int opt = 1; if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("setsockopt error"); } // 向服务器地址结构体中的各个字段赋值 bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(servPort); // 绑定监听套接字和服务器地址 if(bind(listenfd, (struct sockaddr*)&servaddr, socklen) == -1) { perror("bind error"); exit(-1); } if (listen(listenfd, listenq) < 0) { perror("listen error"); return -1; } int i = 0; for (i = 0; i< FD_SETSIZE; i++) clientSockFds[i] = -1; FD_ZERO(&allset); // 情况文件描述符集合 FD_SET(listenfd, &allset); // 将监听套接字加入文件描述符集合 maxfd = listenfd; printf("echo server use select startup, listen on port %d\n", servPort); printf("max connection: %d\n", FD_SETSIZE); for ( ; ; ) { rset = allset; // 将套接字集合赋值给reset // nready是.. nready = select(maxfd + 1, &rset, NULL, NULL, NULL); if (nready < 0) { perror("select error"); continue; } // 如果监听套接字有返回 if (FD_ISSET(listenfd, &rset)) { // 建立连接,返回已连接套接字 connfd = accept(listenfd, (struct sockaddr*) &cliaddr, &socklen); if (connfd < 0) { perror("accept error"); continue; } // 打印对等方信息 sprintf(buf, "accept form %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port); printf(buf, ""); // 将已连接套接字加入套接字存放的数组 for (i = 0; i< FD_SETSIZE; i++) { if (clientSockFds[i] == -1) { clientSockFds[i] = connfd; break; } } // 如果套接字存放数组中没有空闲的地方,则关闭 if (i == FD_SETSIZE) { fprintf(stderr, "too many connection, more than %d\n", FD_SETSIZE); close(connfd); continue; } // 已连接套接字的文件描述符是否是最大的,如果是,则更新 if (connfd > maxfd) maxfd = connfd; // 将已连接套接字放入套接字集合 FD_SET(connfd, &allset); if (--nready <= 0) continue; } // 存放套接字数组,最大套接字文件描述符,套接字集合 handle(clientSockFds, maxfd, &rset, &allset); } } void handle(int * clientSockFds, int maxFds, fd_set* pRset, fd_set* pAllset) { int nread; int i; char buf[MAXLINE]; for (i = 0; i< maxFds; i++) { if (clientSockFds[i] != -1) { if (FD_ISSET(clientSockFds[i], pRset)) { nread = read(clientSockFds[i], buf, MAXLINE);//读取客户端socket流 if (nread < 0) { perror("read error"); close(clientSockFds[i]); FD_CLR(clientSockFds[i], pAllset); clientSockFds[i] = -1; continue; } if (nread == 0) { printf("client close the connection\n"); close(clientSockFds[i]); FD_CLR(clientSockFds[i], pAllset); clientSockFds[i] = -1; continue; } write(clientSockFds[i], buf, nread);//响应客户端 有可能失败,暂不处理 } } } }
实例代码函数流程图
一直觉得函数比较绕,就根据该实例画了流程图,可能有不正确的地方,希望能够被指出