UNP学习_I/O复用之select函数实现回射服务器

UNP学习_I/O复用之select函数实现回射服务器
1、I/O复用模型
??I/O复用是阻塞在select系统调用之上,而不是阻塞在真正的I/O系统调用之上。
UNP学习_I/O复用之select函数实现回射服务器
文章图片

2、函数声明

#/select.h> #/time.h>int select(int maxfdpl, fd_set* readset, //读事件集合 fd_set* writeset, //写事件集合 fd_set* exceptset, //异常事件集合 const struct timeval *timeout ); struct timeval { long tv_sec; //秒 long tv_usec; //微秒 }; //返回值:若有就绪描述符则为其数目,若超时则为0,若出错则为-1 //具体函数介绍参考UNP-P127

3、select实现的tcp回射服务器
// File Name: select_server.c // Author: AlexanderGan // Created Time: Mon 20 Jul 2020 08:32:13 PM CST#include #include #include #include #include #include #include#include #include #include #include #includeint main(int argc, char* argv[]){ if(argc < 2) { printf("eg: ./a.out IP port\n"); } int port = atoi(argv[1]); struct sockaddr_in serv_addr, client_addr; socklen_t serv_len = sizeof(serv_addr); socklen_t cli_len = sizeof(client_addr); //创建套接字 int lfd = socket(AF_INET,SOCK_STREAM,0); printf("lfd = %d\n",lfd); //初始化服务器 memset(&serv_addr,0,serv_len); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(port); //端口复用(防止在一端处于TIME_WAIT状态还未释放端口导致端口被占用的问题) int flag = 1; setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&flag,sizeof(flag)); //绑定IP和端口 if(bind(lfd,(struct sockaddr*)&serv_addr,serv_len) < 0){ perror("bind error"); exit(1); }//设置同时监听的最大个数 listen(lfd,36); printf("Start accept !\n"); //设定最大文件描述符 int maxfd = lfd; //文件描述符读集合 fd_set reads,temp; //初始化读fd_set FD_ZERO(&reads); FD_SET(lfd,&reads); while(1){ temp = reads; int ret = select(maxfd+1,&temp,NULL,NULL,NULL); if(ret == -1) { perror("select error"); exit(1); } //客户端发起了新的连接 if(FD_ISSET(lfd,&temp)){ //接受连接,accept不阻塞 int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len); if(cfd == -1){ perror("accept error"); exit(1); }//接受成功后将客户端地址和端口打印出来 char ip[64]; //存放ip表达式 printf("new client IP:%s, Port: %d\n", inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ip,sizeof(ip)), ntohs(client_addr.sin_port)); //将cfd加入到待检测的读集合 FD_SET(cfd,&reads); //更新最大文件描述符 maxfd = maxfd < cfd ? cfd:maxfd; }//已经连接的客户端有数据到达 for(int t = lfd+1; t <= maxfd; ++t){ if(FD_ISSET(t,&temp)){ char buf[1024] = {0}; int len = recv(t,buf,sizeof(buf),0); if(len == -1){ perror("recv error"); exit(1); } else if(len == 0) { printf("客户端已经断开了连接\n"); close(t); FD_CLR(t,&reads); } else{ //正常接收数据 printf("recv buf is : %s\n",buf); send(t,buf,strlen(buf)+1,0); } } } } close(lfd); return 0; }

4、select函数的优缺点
select优点:
select的最大优势是可以等待多个描述符就绪。
如:在调用recv函数之前,先调用select函数,如果系统没有可读数据那么select函数就会阻塞在这里。当系统存在可读或可写数据时,select函数返回,就可以调用recv函数接 收数据了。
可以看出使用select模型,需要两次调用函数。第一次调用select函数第二次accept函数。使用该模式的好处是:可以等待多个套接字。
【UNP学习_I/O复用之select函数实现回射服务器】select缺点:
(1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。
(2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。
(3)select函数支持的文件描述符数量由FD_SETSIZE设置,默认值是1024。

    推荐阅读