I/O复用之poll服务器
来源:互联网 发布:网络犯罪调查分集剧情 编辑:程序博客网 时间:2024/06/14 00:47
github代码:
https://github.com/NICK-DUAN/Three-U/tree/master/poll_server
代码编写
poll服务器的编写上,就不能直接在代码上做文章了,需要先了解一下poll函数中的几个API和参数。
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
先说返回值,返回值以及timeout这个参数和select的几乎一样,timeout使用单位的是毫秒,表示当前服务器对一个请求最长等待多长时间,nfds表示监听事件集合的大小,定义如下:
typedef unsigned long int nfds_t
至于struct pollfd *fds,这个才是poll的核心所在,系统的定义为:
struct pollfd { int fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ };
fd表示的当然就是每个文件描述符,events和revents是一系列的位表示法,events表示的是,我们需要监听同一结构体的fd的什么事件,revents由内核修改,表示的是当前结构体中的fd上究竟发生了事件。其中,events和revents可以表示的事件如下所示:
POLLIN There is data to read. POLLPRI There is urgent data to read (e.g., out-of-band data on TCP socket; pseudoterminal master in packet mode has seen state change in slave). POLLOUT Writing is now possible, though a write larger that the available space in a socket or pipe will still block (unless O_NONBLOCK is set). POLLRDHUP tream socket peer closed connection, or shut down writing half of connection. The _GNU_SOURCE fea‐ture test macro must be defined POLLERR Error condition (only returned in revents; ignored in events). POLLHUP Hang up (only returned in revents; ignored in events). Note that when reading from a channel suchas a pipe or a stream socket, this event merely indicates that the peer closed its end of the chan‐el. Subsequent reads from the channel will return 0 (end of file) only after all outstanding data in the channel has been consumed. POLLNVAL Invalid request: fd not open (only returned in revents; ignored in events). POLLRDNORM Equivalent to POLLIN. POLLRDBAND Priority band data can be read (generally unused on Linux). POLLWRNORM Equivalent to POLLOUT. POLLWRBAND Priority data may be written.
对应到中文就是:
所以我们在编写代码时只需要设置一个struct pollfd的数组即可,这个数组可以告诉系统我们需要监听那个套接字,我们需要监听这个套接字的什么事件,而这个套接字上又具体发生了什么事件,而不用像select那样,每次发生事件之后,设定一个文件描述符到全部数据中,等待下一次的遍历发现并处理,并且在使用完之后重置这个文件描述符,这都是比较耗费时间的。
#include <stdio.h>#include <stdlib.h>#include <sys/poll.h>#include <string.h>#include <sys/socket.h>#include <sys/types.h>#include <arpa/inet.h>#include <netinet/in.h>#define SIZE 128void usage(char* str){ printf("%s [local_ip] [local_port]\n",str);}int startup(const char* ip,const char* port){ int sock=socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("sockek"); exit(2); } struct sockaddr_in local; local.sin_family=AF_INET; local.sin_port=htons(atoi(port)); local.sin_addr.s_addr=inet_addr(ip); if(bind (sock,(struct sockaddr*)&local,sizeof(local))<0 ) { perror("bind"); exit(3); } if(listen(sock,10)<0) { perror("listen"); exit(4); } return sock;}int main(int argc,char* argv[]){ if(argc!=3) { usage(argv[0]); exit(1); } int listen_sock=startup(argv[1],argv[2]); int i=0; int max=0; int timeout=3000; struct pollfd readfds[SIZE]; for(i=0;i<SIZE;i++){ readfds[i].fd=-1; readfds[i].events=0; readfds[i].revents=0; }
依旧是初始化,需要将readfds的套接字,监听与发生事件均初始化,否则在之后的代码编程中会有不必要的错误发生。
readfds[0].fd=listen_sock; readfds[0].events = POLLIN;
并且将监听套接字放在第一位,保证监听套接字的存在,这样才能在进入后判断是否一个已经到来。
while(1){ int ret = poll(readfds,SIZE,timeout); switch(ret){ case -1: perror("poll"); break; case 0: printf("timeout...\n"); break; default:{ for(i=0;i<SIZE;i++){ if(i==0 && ( (readfds[i].revents) & POLLIN)){//listen_sock ready
此处使用revents的原因时,在poll函数时使用的时events表示需要监听当前套接字的事件,当poll被成功触发,就将当前套接字上实际发生了什么事件写在了revents上,所以此处的判定使用revents。表示监听套接字上发生可读事件,意味着有连接到来。
struct sockaddr_in client; socklen_t len=sizeof(client); int new_sock=accept(readfds[i].fd,(struct sockaddr*)&client,&len); if(new_sock<0){ perror("accept"); break; } printf("get a client:[%s__%d]\n",inet_ntoa(client.sin_addr),htons(client.sin_port)); int j=1; for(;j<SIZE;j++){ if(readfds[j].fd==-1){ readfds[j].fd=new_sock; readfds[j].events=POLLIN; break; } }if(j==SIZE){ printf("connect full...\n"); close(new_sock); }
这些代码同select相似,也是在找到一个合适的位置来表示当前的文件描述符。
}else if(i!=0 && ( (readfds[i].revents) & POLLIN)){//client ready char buff[1024]; ssize_t s=read(readfds[i].fd,buff,sizeof(buff)-1); if(s>0){ buff[s]=0; printf("client say# %s",buff); write(readfds[i].fd,buff,sizeof(buff)-1); }else if(s==0){ printf("client quit...\n"); close(readfds[i].fd); readfds[i].fd=-1; } else{ perror("read"); } }//end of elif }//end of for }//end of default }//end of switch }//end of while return 0;}
其实,完完全全的写一次select代码,再将poll中的struct pollfd搞明白,以及为什么使用的是revents,那么poll服务器你已经懂了70%了。
- I/O复用之poll服务器
- I/O复用模型之poll
- I/O复用之poll
- I/O复用之poll模型
- I/O多路复用之poll服务器
- I/O复用之poll系统调用
- I/O复用系统调用之select()和poll()
- Linux网络编程---I/O复用模型之poll
- LINUX系统I/O复用技术之二:poll()
- I/O复用之select、poll、epoll函数
- I/O多路转接之poll服务器
- I/O多路复用之poll
- I/O 多路复用之poll
- I/O多路复用之poll
- I/O多路复用之poll
- I/O多路复用之poll
- I/O多路复用之poll
- I/O多路复用之poll
- jquery 调用 click 事件 的 三种 方式
- Mac安装lua环境配置
- java非捕获组-肯定式向前查找(?=)和肯定式向后查找(?<=)
- java8 快速实现List转map 、分组、过滤等操作
- Sublime Text 高级用法
- I/O复用之poll服务器
- CCF 201609-1 最大波动
- 小程序支付详解+源码(客户端+服务端)
- IOS打包ipa
- C条件编译在软件开发中的运用
- 如何去设计一个自适应的网页设计或HTMl5
- 笛卡尔树
- 以注解的形式来实现一个springmvc的简单范例
- 21. Merge Two Sorted Lists