python中的select相关介绍

来源:互联网 发布:linux 源码安装 编辑:程序博客网 时间:2024/06/05 23:52

本文转载自点击打开链接


select同时监控多个sockets,支持网络服务和多个客户端通信。
该模块可以访问大多数操作系统中的select()和poll()函数, Linux2.5+支持的epoll()和大多数BSD支持的kqueue()。请注意,在Windows上,它仅适用于socket,在其他操作系统上,它也适用于其他类型的文件(特别是在Unix上,它还可以用于管道)。它不能用于确定常规文件是否变化。

select同时监控多个sockets,支持网络服务和多个客户端通信。

以下内容部分来源于:http://www.cnblogs.com/coser/archive/2012/01/06/2315216.html。

该模块可以访问大多数操作系统中的select()和poll()函数, Linux2.5+支持的epoll()和大多数BSD支持的kqueue()。请注意,在Windows上,它仅适用于select,在其他操作系统上,它也适用于其他类型的文件(特别是在Unix上,它还可以用于管道)。它不能用于确定常规文件是否变化。

Select:synchronous I/O multiplexing. select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。Select是跨平台的。select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。

Poll:wait for some event on a file descriptor。poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候 将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。不过poll并不适用于windows平台。

Epoll:I/O event notification facility。Linux 2.5.44以后支持,是性能最好的多路I/O就绪通知方法。epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描 述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进 行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制, 迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

Kqueue:暂不涉及。


select监控sockets,打开的文件,管道(任何有fileno()方法返回一个有效的文件描述符的东东),直到它们变成可读可写或发生通讯错误。它可以更容易地同时监控多个连接,比使用socket超时轮询更有效,因为监控在操作系统网络层而不是python解析器。注意windows的select并不支持打开文件。

下面代码通过select监控多个连接。注意通过server.setblocking(0)设置为非阻塞模式。select()的参数为3个列表:第一列表为读取输入数据的对象;第2个接收要发送的数据,第3个存放errors。select()返回的3个列表和输入类似:readable,writable,exceptional。

readable有3种可能:对于用来侦听连接主服务器socket,表示已准备好接受一个到来的连接;对于已经建立并发送数据的链接,表示有数据到来;如果没数据到来,表示链接已经关闭。

writable的情况:连接队列中有数据,发送下一条消息。如果队列中无数据,则从output队列中删除。

socket有错误,也要从output队列中删除。

'''Created on 2012-1-6The echo server example from the socket section can be extanded to watche for more thanone connection at a time by using select() .The new version starts out by creating a nonblockingTCP/IP socket and configuring it to listen on an address@author: xiaojay'''import selectimport socketimport Queue #create a socketserver = socket.socket(socket.AF_INET,socket.SOCK_STREAM)server.setblocking(False)#set option reusedserver.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR  , 1) server_address= ('192.168.1.102',10001)server.bind(server_address) server.listen(10) #sockets from which we except to readinputs = [server] #sockets from which we expect to writeoutputs = [] #Outgoing message queues (socket:Queue)message_queues = {} #A optional parameter for select is TIMEOUTtimeout = 20 while inputs:    print "waiting for next event"    readable , writable , exceptional = select.select(inputs, outputs, inputs, timeout)     # When timeout reached , select return three empty lists    if not (readable or writable or exceptional) :        print "Time out ! "        break;        for s in readable :        if s is server:            # A "readable" socket is ready to accept a connection            connection, client_address = s.accept()            print "    connection from ", client_address            connection.setblocking(0)            inputs.append(connection)            message_queues[connection] = Queue.Queue()        else:            data = s.recv(1024)            if data :                print " received " , data , "from ",s.getpeername()                message_queues[s].put(data)                # Add output channel for response                    if s not in outputs:                    outputs.append(s)            else:                #Interpret empty result as closed connection                print "  closing", client_address                if s in outputs :                    outputs.remove(s)                inputs.remove(s)                s.close()                #remove message queue                 del message_queues[s]    for s in writable:        try:            next_msg = message_queues[s].get_nowait()        except Queue.Empty:            print " " , s.getpeername() , 'queue empty'            outputs.remove(s)        else:            print " sending " , next_msg , " to ", s.getpeername()            s.send(next_msg)         for s in exceptional:        print " exception condition on ", s.getpeername()        #stop listening for input on the connection        inputs.remove(s)        if s in outputs:            outputs.remove(s)        s.close()        #Remove message queue        del message_queues[s]                       

0 0
原创粉丝点击