【问题】

select,poll,epoll的区别是什么? select的并发处理是源于BSD系统的支持,poll是商业公司的unix版本SystemV 提供支持。epoll是在Linux 2.5+开始支持的。就像message queue都有BSD和SystemV版本的API。select是处理网络并发处理,与poll的区别是有文件句柄上线限制。poll不会因为打开文件的增多而降低效率。

【select功能概述】

一切复杂的问题都是简单问题叠加组合,引用一下英文文档原文。
“This module provides access to the select() and poll() functions available in most operating systems, epoll() available on Linux 2.5+ and kqueue() available on most BSD. Note that on Windows, it only works for sockets; on other operating systems, it also works for other file types (in particular, on Unix, it works on pipes). It cannot be used on regular files to determine whether a file has grown since it was last read.”

server.py
[code] import socket, select; import time; import os;

host = “127.0.0.1”
port = 1688

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#通过指定第三个参数“IPPROTO_TCP/IPPROTO_UDP”, 指定使用"TCP/UDP"协议进行传输!
s.bind((host, port))
s.listen(5)

while True:
inputfds, outputfds, errorfds = select.select([s,],[],[],5)
if len(inputfds) !=0:
clientsock, clientaddr = s.accept()
buf = clientsock.recv(8024)
if len(buf) != 0:
print (buf)
clientsock.close()

[/code]

client.py
[code] import socket, select host = "127.0.0.1" port = 1688 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) s.send("the Candy Web server on SAE of sina!") s.close() [/code]

【API说明】

select的作用是“Waiting for I/O completion.”一般会和socket的API结合处理网络异步并发,但是也可以不和socket联系在一起应用。核心的API函数是select,结合几个关键的宏使用。FD_ZERO, FD_SET, FD_CLR,FD_ISSET。通过一套的函数和宏管理用户的文件描述符集合。(SETS)

英文文档总是能直戳这些宏的本质。
" Four macros are provided to manipulate the sets. FD_ZERO() clears a set. FD_SET() and FD_CLR() respectively add and remove a given file descriptor from a set. FD_ISSET() tests to see if a file descriptor is part of the set; this is useful after select() returns.
"

【宏定义】

FD_ZERO():清空文件集。 FD_SET():添加句柄到集合。 FD_CLR():从集合中清除句柄。 FD_ISSET():判断句柄是否在集合中。

【Select接口说明】

1.int n 2.fd_set * readfds 3.fd_set * writefds 4.fd_set * exceptfds 5.struct timeval * timeout timeout == NULL: 一直等待直到事件发生。 timeout > 0: 按设定的大于0的时间值进行等待。 timeout = 0: 没有时间等待,直接返回。

【选读】

"select(I/O多工机制) 定义函数 int select(int n,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout); 函数说明 select()用来等待文件描述词状态的改变。参数n代表最大的文件描述词加1,参数readfds、writefds 和exceptfds 称为描述词组,是用来回传该描述词的读,写或例外的状况。底下的宏提供了处理这三种描述词组的方式: FD_CLR(inr fd,fd_set* set);用来清除描述词组set中相关fd 的位 FD_ISSET(int fd,fd_set *set);用来测试描述词组set中相关fd 的位是否为真 FD_SET(int fd,fd_set*set);用来设置描述词组set中相关fd的位 FD_ZERO(fd_set *set);用来清除描述词组set的全部位" 看到,“多工机制”和类似"是用来回传该描述词的读,写或例外的状况。"这种文字,直接就被干倒了,难道原文英语就这么晦涩不堪吗?残酷的阅读体验。

【例子A:读取键盘输入的数据】

在Linux系统中,键盘设备也被标识为文件,找来一个例子,很简短的/dev/tty键盘设备和select的联合使用。这么做的目的,就是很单纯的演示select本身相关函数和使用。

keyboard.c
[code]
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<fcntl.h>
#include<sys/select.h>

int main(int argc, char **argv)
{
int keyboard; //键盘的文件句柄
int ret,i;
char c;
fd_set readfd; //读操作的集合的定义
struct timeval timeout; // 提供给select函数使用的,延时时间结构体,告知需要等待的时间长度
keyboard = open(“/dev/tty”, O_RDONLY | O_NONBLOCK);//读取键盘缓冲区里的数据
assert(keyboard>0);
while(1) //主循环
{
timeout.tv_sec = 1; // 等待的时间设定
timeout.tv_usec = 0;
FD_ZERO(&readfd); // 初始化读操作集合。
FD_SET(keyboard, &readfd); //把键盘文件句柄填入读文件集
ret = select(keyboard+1, &readfd, NULL, NULL, &timeout); // 取得外部IO的状态数据
if (FD_ISSET(keyboard, &readfd)) //判断读集合中的keyboard句柄是否准备好了,可以工作。
{
i = read(keyboard, &c, 1); //读取键盘文件
if (‘\n’ == c)
continue;
printf(“input is %c\n”, c);
if (‘q’ == c)
break;

            }
    }

}
[/code]

server.cpp
client.cpp

【结尾】

也许我应该长期的维护这个文档,回头看看对细节表述曾经放下的错误,对说的不是人话的地方,进行修改,并且围绕主题加入更高级的应用内容。

作者:小盛
注释:个人劳动成果,转载使用请注明本文作者及出处链接,谢谢合作!
Python与C++ select API的对比

参考文献:
Python官方文档