《unix网络编程》(15)poll函数以及使用poll的客户服务器程序
2015-04-06 14:40
323 查看
poll和select对比
select存在一个明显缺陷:<sys/select.h>中定义的FD_SETSIZE常值是数据类型fd_set中描述符总数,其值通常为1024。当今的Unix版本允许每个进程使用无限数目的描述符(受限于内存总量和管理性),然而增加描述符集大小的唯一方法是先增大FD_SETSIZE,而要重新编译内核才行。此外,在文章《unix网络编程》(14)使用select、shutdown的客户服务器程序中提到,文章中的服务器程序要分配一个client数组以及一个rset的描述符集。
而poll可以避免select存在的这两个问题。对于poll来说,分配一个pollfd结构的数组并把该数组中元素的数目通知内核成了调度者的责任。内核不再需要知道类似fd_set的固定大小的数据类型。
poll函数
#include <poll.h> int poll(struct pollfd *fds, nfds_t nfds, int timeout); //返回:如有就绪描述符就返回其数目,超时返回0,若出错返回-1.参数:
(1)第一个参数是指向一个结构数组第一个元素的指针。每个元素是一个pollfd结构,用于指定测试某个给定描述符fd的条件。其后两个成员避免了使用值-结果参数,select的中间三个参数都是值-结果参数。下图是指定这两个参数的一些常值。
poll识别三类数据:普通(normal)、优先级带(priority band)、高优先级(high priority)
//pollfd结构体 struct pollfd { int fd; /* file descriptor :要测试的描述符*/ short events; /* requested events: 要测试的事件 */ short revents; /* returned events : 返回该描述符的状态*/ };
然而,在我的Ubuntu系统中给出的如下图的解释,因此我没在本文后面的程序中使用POLLRDNORM常值,而使用了POLLIN(定义为POLLRDNORM和POLLRDBAND的逻辑或)。
(2)第二个参数nfds:指定第一个参数结构数组中元素的个数。
(3)第三个参数timeout:poll函数返回前等待的时间,是毫秒数的正数。
如果不关心某个特定描述符,可以把它对应的pollfd结构的fd成员设为负值,这样poll函数将忽略这样的pollfd结构的events成员,返回时将revents置为0.
使用poll的服务器端程序
//该版本使用poll代替select #include "myheader.h" #define OPEN_MAX 1024 //《Unix网络编程》上说该值在<limits.h>中定义但我的系统未定义 int main(int argc, char **argv) { int i, maxi, listenfd, connfd, sockfd; int nready; ssize_t n; char buf[MAXLINE]; char cliip[MAXLINE]; //作为inet_ntop的第三个参数,返回点分十进制ip socklen_t clilen; //我们声明在pollfd结构数组中存在OPEN_MAX个元素 //确定一个进程任何时刻打开的描述符最大值并不容易, //方法之一就是以参数_SC_OPEN_MAX调用POSIX的sysconf函数, //然后动态分配一个合适大小的数组。但是该函数的返回可能是 //“indeterminate”(不确定),因此这里使用POSIX的OPEN_MAX常值。 struct pollfd client[OPEN_MAX]; struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (const struct sockaddr*)&servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); //将client数组第一项用于监听套接字,其他项描述符置为-1; //将第一项设置我POLRDNORM事件,这样有新的连接准备好被接受时poll会通知我们。 client[0].fd = listenfd; client[0].events = POLLIN; for (i = 1; i < OPEN_MAX; i++) client[i].fd = -1; maxi = 0; //数组client[]正在使用下标的最大值 for ( ; ; ) { //调用poll以等待新连接或现有连接上有数据可读 //当一个新连接被接受后,在client中查找第一个描述符成员为负的可用项 //从下标1开始,因为client[0]固定用于监听套接字。找到一个可用项后, //将新连接的描述符保存在其中,并设置POLLIN事件。 nready = Poll(client, maxi + 1, -1); if (client[0].revents & POLLIN) { clilen = sizeof(cliaddr); connfd = Accept(listenfd, (const struct sockaddr*)&cliaddr, &clilen); printf("new client: %s, port %d \n", Inet_ntop(AF_INET, &cliaddr.sin_addr, cliip, sizeof(buf)), ntohs(cliaddr.sin_port)); for (i = 1; i < OPEN_MAX; i++) { if (client[i].fd < 0) { client[i].fd = connfd; break; } } if (i == OPEN_MAX) err_quit("too many clients"); client[i].events = POLLIN; if (i > maxi) maxi = i; if (--nready <= 0) continue; } //检查两个返回事件:POLLIN和POLLERR。没有在event成员中 //设置第二个事件,因为它在条件成立时总是返回。我们检查POLLERR //的原因是:有些实现在一个连接上接收到RST时返回的是POLLERR事件,而 //其他知识POLLIN事件。不论哪种情形,都调用read,当错误发生时, //read将返回这个错误。当一个现有连接由客户终止时,将其fd成员置为-1. for (i = 1; i <= maxi; i++) { if ((sockfd = client[i].fd) < 0) continue; if (client[i].revents & (POLLRDNORM| POLLERR)) { if ((n = read(sockfd, buf, MAXLINE)) < 0) { if (errno == ECONNRESET) { //connection reset by client Close(sockfd); client[i].fd = -1; } else err_sys("read error"); } else if (n == 0) { //connection closed by client Close(sockfd); client[i].fd = -1; } else Writen(sockfd, buf, n); if (--nready <= 0) break; } } } }
完整源码下载
http://download.csdn.net/detail/u013074465/8567889github源码:
服务器端 https://github.com/liyangddd/linux_practice/blob/master/network_programming/tcpsrvepoll.c
客户端 https://github.com/liyangddd/linux_practice/blob/master/network_programming/tcpclipoll.c
相关文章推荐
- 练习1-15 重新编写1.2节中的温度转换程序,使用函数实现温度转换计算
- 学会使用函数编程,以及设计比较完备的测试用例完成对小程序的测试
- 《UNIX网络编程 卷1》 笔记: 使用poll函数的单进程TCP回射服务器程序
- QT学习之阻拦退出程序函数以及事件过滤器的使用,以及文件的操作。
- 学会使用函数编程,以及设计比较完备的测试用例完成对小程序的测试
- 《unix网络编程》(14)使用select、shutdown的客户服务器程序
- Reporting Service Tips 101(#4) - 使用RS实现报表的自动生成以及在程序中调用RS
- javascript入门系列演示·函数的定义以及简单参数使用,调用函数
- 使用CreateProcess函数运行其他程序
- javascript入门系列演示·函数的定义以及简单参数使用,调用函数 .txt
- MFC程序中消息以及函数的处理顺序简介
- 使用单独的命令处理类来处理命令消息(适用于有很多命令处理函数的对象,以及共享命令处理函数)
- 封装了envi函数的IDL发布程序的执行方法 以及注意事项
- linux 系统下使用C程序实现时钟的函数
- 线程函数的设计以及MsgWaitForMultipleObjects函数的使用要点
- 对引用和指针使用以及函数返回引用和指针类型的理解
- 您可以在您的asp程序中使用如下函数来进行在线发信
- 在VS2005中采用c#书写存储过程和函数等以及使用
- 显示程序使用最大内存函数
- 自己写感觉比较好且使用的的函数 包括浏览目录以及 Slplit函数