前言

  在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 )。但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设置为监听位的位。

有没有方法简化这个处理呢?

有!在《UNIX网络编程》第六章最后介绍了一种类似select的函数:poll函数,用它来实现IO复用使代码简化了不少( 起码并发回射服务器的例子是的 )。

poll函数介绍

1. 包含头文件:<poll.h>

2. 函数原型:int poll ( struct pollfd * fdarray, unsigned long nfds, int timeout);

 // 监听描述数组的元素
struct pollfd {
int fd; // 监听描述符
short events; // 测试事件
short revents; // 测试结果
};

3. 说明:相比于select函数,poll函数使用了结构体数组fdarray来表示监听描述符集合,该数组元素类型如上代码所示。当我们检索这个数组,我们可以知道有哪些描述符被监听,监听测试事件是什么,测试的结果又是什么。(而在select函数的fdset描述符集合中,无法获知某位是不是被监听的描述符,这也就是下面的代码并不需要使用client数组的原因)。

代码实现

 #include    "unp.h"
// 下头文件包含宏定义OPEN_MAX
#include <limits.h> int
main(int argc, char **argv)
{
int i, maxi, listenfd, connfd, sockfd;
int nready;
ssize_t n;
char buf[MAXLINE];
socklen_t clilen;
struct pollfd client[OPEN_MAX];
struct sockaddr_in cliaddr, servaddr; listenfd = Socket(AF_INET, SOCK_STREAM, ); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); Listen(listenfd, LISTENQ); client[].fd = listenfd;
client[].events = POLLRDNORM;
// 清空监听描述符数组
for (i = ; i < OPEN_MAX; i++)
client[i].fd = -;
// 该变量表示已连接套接字描述符的最大数量( 曾经 )
maxi = ; for ( ; ; ) {
nready = Poll(client, maxi+, INFTIM); // 如果有监听描述符检测到可读数据的信号
if (client[].revents & POLLRDNORM) {
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
#ifdef NOTDEF
printf("new client: %s\n", Sock_ntop((SA *) &cliaddr, clilen));
#endif // 登记刚刚获取到的已连接套接字描述符
// OPEN_MAX表示最多监听的描述符个数
for (i = ; i < OPEN_MAX; i++)
if (client[i].fd < ) {
client[i].fd = connfd;
break;
}
if (i == OPEN_MAX)
err_quit("too many clients"); // 设置监听测试事件
client[i].events = POLLRDNORM;
if (i > maxi)
maxi = i; // 如果信号已经处理完毕则自动进入下一次循环
if (--nready <= )
continue;
} for (i = ; i <= maxi; i++) {
if ( (sockfd = client[i].fd) < )
continue;
// 如果检测到可读或者发生错误的信号
if (client[i].revents & (POLLRDNORM | POLLERR)) {
if ( (n = read(sockfd, buf, MAXLINE)) < ) {
if (errno == ECONNRESET) {
#ifdef NOTDEF
printf("client[%d] aborted connection\n", i);
#endif
Close(sockfd);
client[i].fd = -;
} else
err_sys("read error");
} else if (n == ) {
#ifdef NOTDEF
printf("client[%d] closed connection\n", i);
#endif
Close(sockfd);
client[i].fd = -;
} else
Writen(sockfd, buf, n); // 如果检测到可读或者发生错误的信号
if (--nready <= )
break;
}
}
}
}

说明

如果从可移植性方面考虑或者有处理信号阻塞方面的需求(要知道select还有个作为其升级版的pselect函数能够妥善处理信号阻塞),就还是得用select函数而不是poll函数。否则的话就随意了。

第二十篇:不为客户连接创建子进程的并发回射服务器(poll实现)的更多相关文章

  1. 不为客户连接创建子进程的并发回射服务器( poll实现 )

    前言 在上文中,我使用select函数实现了不为客户连接创建子进程的并发回射服务器( 点此进入 ).但其中有个细节确实有点麻烦,那就是还得设置一个client数组用来标记select监听描述符集中被设 ...

  2. 第十九篇:不为客户连接创建子进程的并发回射服务器(select实现)

    前言 在此前,我已经介绍了一种并发回射服务器实现.它通过调用fork函数为每个客户请求创建一个子进程.同时,我还为此服务器添加了自动消除僵尸子进程的机制.现在请想想,在客户量非常大的情况下,这种为每个 ...

  3. 不为客户连接创建子进程的并发回射服务器( select实现 )

    前言 在此前,我已经介绍了一种并发回射服务器实现( 点此进入 ).它通过调用fork函数为每个客户请求创建一个子进程.同时,我还为此服务器添加了自动消除僵尸子进程的机制.现在请想想,在客户量非常大的情 ...

  4. Python开发【第二十篇】:缓存

    Python开发[第二十篇]:缓存redis&Memcache   点击这里 Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy ...

  5. Egret入门学习日记 --- 第二十篇(书中 9.1~9.3 节 内容 组件篇)

    第二十篇(书中 9.1~9.3 节 内容 组件篇) 第八章中的内容. 以上都是基本的Js知识,我就不录入了. 直接来看 第9章. 开始 9.1节. 以上内容告诉你,Egret官方舍弃了GUI,使用了E ...

  6. Python之路【第二十篇】:待更新中.....

    Python之路[第二十篇]:待更新中.....

  7. TCP客户/服务器程序实例——回射服务器

    目录 客户/服务器程序源码 POSIX信号处理 POSIX信号语义 处理SIGCHLD信号 处理僵死进程 处理被中断的系统调用 wait和waitpid函数 wait和waitpid函数的区别 网络编 ...

  8. Vue学习之路第二十篇:Vue生命周期函数-组件创建期间的4个钩子函数

    1.每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听.编译模板.将实例挂载到 DOM 并在数据变化时更新 DOM 等.同时在这个过程中也会运行一些叫做生命周期钩子的函数 ...

  9. Python之路(第二十篇) subprocess模块

    一.subprocess模块 subprocess英文意思:子进程 那什么是进程呢? (一)关于进程的相关理论基础知识 进程是对正在运行程序的一个抽象,进程的概念起源于操作系统,是操作系统最核心的概念 ...

随机推荐

  1. Android学习之发送及接收广播

    1.使用标准广播 1.1 定义广播接收器 public class MyBroadcastReceiver extends BroadcastReceiver { @Override public v ...

  2. 9、Qt 事件处理机制

    原文地址:http://mobile.51cto.com/symbian-272812.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生 ...

  3. 关于Dijkstra算法

    Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Di ...

  4. Axiom3D:手动创建ManualObject与Mesh,以及如何使用Cg着色器语言

    在开始正文前,先说下Axiom3D里遇到的二个BUG. 1.在启动axiom生成的程序中,我发现输出里总是有一些如"billboard_type","billboard_ ...

  5. GLSL着色语言学习。橙皮书第一个例子GLSL+OpenTK+F#的实现。

    Opengl红皮书有选择的看了一些,最后的讲着色语言GLSL的部分看的甚为不理解,然后找到Opengl橙皮书,然后就容易理解多了. 在前面,我们或多或少接触到Opengl的处理过程,只说前面一些处理, ...

  6. android wifi RSSI达到阈值自动断开

    设置wifi的RSSI达到阈值之后自动断开. wifi状态改变,会更新状态栏,在状态栏中更改. --- a/packages/SystemUI/src/com/android/systemui/sta ...

  7. Spring Data Redis学习

    本文是从为知笔记上复制过来的,懒得调整格式了,为知笔记版本是带格式的,内容也比这里全.点这里 为知笔记版本 Spring Data Redis 学习 Version 1.8.4.Release 前言 ...

  8. Java的图形用户界面的基本工具

    AWT(Abstract Window Toolkit),中文译为抽象窗口工具包,该包提供了一套与本地图形界面进行交互的接口,是Java提供的用来建立和设置Java的图形用户界面的基本工具. AWT中 ...

  9. Java EE的十三个技术规范

    Java 是一种非常棒的语言,健壮,跨平台运行,屏蔽了具体的平台环境的要求,也就是说只要支持java 虚拟机,就可以运行java程序. 下面,我们一起学习一下J2EE的十三种技术规范. 一.JDBC: ...

  10. linux的awk命令解读

    转自:http://blog.csdn.net/guoer9973/article/details/44650729 awk是行处理器: 相比较屏幕处理的优点,在处理庞大文件时不会出现内存溢出或是处理 ...