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

【UNP学习_I/O复用之poll函数实现回射服务器】UNP学习_I/O复用之poll函数实现回射服务器
1、函数原型:

#includestruct pollfd{ int fd; //文件描述符 short events; //等待的事件 short revents; //实际发生的事件 }; int poll(struct pollfd *fds,nfds_t nfds,int timeout); //fds: 数组地址 //nfds: 数组的最大长度,数组中最后一个元素下标+1。内核会轮询fd数组中的每个文件描述符 //timeout: -1 : 永久阻塞;0 :调用完成立刻返回; >0等待的时长(ms) //返回值:I/O发生变化的文件描述符的个数

2、实现代码
// File Name: poll_server.c // Author: AlexanderGan // Created Time: Wed 29 Jul 2020 04:31:41 PM CST#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); //绑定ip和端口 bind(lfd,(struct sockaddr*)&serv_addr,serv_len); //设置同时监听的最大个数 listen(lfd,36); printf("Start accept !\n"); //poll结构体 struct pollfd allfd[1024]; int max_index = 0; //初始化 for(int i = 0; i < 1024; i++){ allfd[i].fd = -1; } allfd[0].fd = lfd; allfd[0].events = POLLIN; while(1) { int i = 0; int ret = poll(allfd,max_index+1,-1); if(ret == -1) { printf("poll error!\n"); exit(1); }if(allfd[0].revents & POLLIN){ int cfd = accept(lfd,(struct sockaddr*)&client_addr,&cli_len); printf("======start======,cfd= %d\n",cfd); //cfd添加到poll数组 for(i; i < 1024; ++i){if(allfd[i].fd == -1) { printf("i = %d\n",i); allfd[i].fd = cfd; break; }} allfd[i].events = POLLIN; //更新max_index max_index = max_index < i ? i : max_index; printf("max_index = %d\n",max_index); }//遍历数组 for(i = 1; i <= max_index; i++){ int fd = allfd[i].fd; printf("i = %d,fd = %d\n",i,fd); if(fd == -1) continue; bool res = (allfd[i].revents & POLLIN); printf("res = %d\n",(int)res); if(allfd[i].revents & POLLIN){ printf("recv!"); //接受数据 char buf[1024] = {0}; int len = recv(fd,buf,sizeof(buf),0); printf("len == %d\n", len); if(len == -1) { perror("recv error"); exit(1); } else if(len == 0) { allfd[i].fd = -1; close(fd); printf("客户端已主动断开连接。\n"); } else { printf("recv buf = %s\n",buf); //小写全变大写 for(int k = 0; k < len; ++k) { buf[k] = toupper(buf[k]); } printf("buf toupper = %s\n",buf); send(fd,buf,strlen(buf)+1,0); } }} } close(lfd); return 0 ; }

3、poll的优缺点
优点:
(1)可以等待多个描述符就绪。
(2)poll没有描述符数量的上限限制,因为在内核中它的遍历线性结构是基于链表来存储的。
缺点:
(1)每次调用poll,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。
(2)同时每次调用poll都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。
(3)poll不能跨平台,可移植性差。
(4)只支持水平触发。

    推荐阅读