首先列一下,sellect、poll、epoll三者的区别 
select 
select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。

select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。

另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。

poll 
poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。

poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。

epoll 
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。

epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。

另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

使用 select : 
在python中,select函数是一个对底层操作系统的直接访问的接口。它用来监控sockets、files和pipes,等待IO完成(Waiting for I/O completion)。当有可读、可写或是异常事件产生时,select可以很容易的监控到。 
select.select(rlist, wlist, xlist[, timeout]) 传递三个参数,一个为输入而观察的文件对象列表,一个为输出而观察的文件对象列表和一个观察错误异常的文件列表。第四个是一个可选参数,表示超时秒数。其返回3个tuple,每个tuple都是一个准备好的对象列表,它和前边的参数是一样的顺序。下面,主要结合代码,简单说说select的使用。 
Server端程序: 
1、该程序主要是利用socket进行通信,接收客户端发送过来的数据,然后再发还给客户端。 
2、首先建立一个TCP/IP socket,并将其设为非阻塞,然后进行bind和listen。 
3、通过select函数获取到三种文件列表,分别对每个列表的每个元素进行轮询,对不同socket进行不同的处理,最外层循环直到inputs列表为空为止 
4、当设置timeout参数时,如果发生了超时,select函数会返回三个空列表

Python Select Server,可监控事件数量有限制:

#!/usr/bin/python
# -*- coding: utf- -*-
import select
import socket
import Queue server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(False)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR , )
server_address= ('192.168.1.5',)
server.bind(server_address)
server.listen() #select轮询等待读socket集合
inputs = [server]
#select轮询等待写socket集合
outputs = []
message_queues = {}
#select超时时间
timeout = while True:
print "等待活动连接......"
readable , writable , exceptional = select.select(inputs, outputs, inputs, timeout) if not (readable or writable or exceptional) :
print "select超时无活动连接,重新select...... "
continue;
#循环可读事件
for s in readable :
#如果是server监听的socket
if s is server:
#同意连接
connection, client_address = s.accept()
print "新连接: ", client_address
connection.setblocking()
#将连接加入到select可读事件队列
inputs.append(connection)
#新建连接为key的字典,写回读取到的消息
message_queues[connection] = Queue.Queue()
else:
#不是本机监听就是客户端发来的消息
data = s.recv()
if data :
print "收到数据:" , data , "客户端:",s.getpeername()
message_queues[s].put(data)
if s not in outputs:
#将读取到的socket加入到可写事件队列
outputs.append(s)
else:
#空白消息,关闭连接
print "关闭连接:", client_address
if s in outputs :
outputs.remove(s)
inputs.remove(s)
s.close()
del message_queues[s]
for s in writable:
try:
msg = message_queues[s].get_nowait()
except Queue.Empty:
print "连接:" , s.getpeername() , '消息队列为空'
outputs.remove(s)
else:
print "发送数据:" , msg , "到", s.getpeername()
s.send(msg) for s in exceptional:
print "异常连接:", s.getpeername()
inputs.remove(s)
if s in outputs:
outputs.remove(s)
s.close()
del message_queues[s]
Client端程序:
Client端创建多个socket进行server链接,用于观察使用select函数的server端如何进行处理。 #!/usr/bin/python
# -*- coding: utf-8 -*- import socket messages = ["This is the message" ,
"It will be sent" ,
"in parts "] print "Connect to the server" server_address = ('192.168.1.5',8080) #Create a TCP/IP sock socks = [] for i in range(10):
socks.append(socket.socket(socket.AF_INET,socket.SOCK_STREAM)) for s in socks:
s.connect(server_address) counter = 0
for message in messages :
#Sending message from different sockets
for s in socks:
counter+=1
print " %s sending %s" % (s.getpeername(),message+" version "+str(counter))
s.send(message+" version "+str(counter))
#Read responses on both sockets
for s in socks:
data = s.recv(1024)
print " %s received %s" % (s.getpeername(),data)
if not data:
print "closing socket ",s.getpeername()
s.close() 注:
需客户端send之后。
getpeername可以获得服务器的地址信息和端口号,正好和getsockname获得本机地址信息和端口号完全相反

Python Poll Server,Select升级版,无可监控事件数量限制,还是要轮询所有事件:

#!/usr/bin/python
# -*- coding: utf- -*-
import socket
import select
import Queue server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, )
server_address = ("192.168.1.5", )
server.bind(server_address)
server.listen()
print "服务器启动成功,监听IP:" , server_address
message_queues = {}
#超时,毫秒
timeout =
#监听哪些事件
READ_ONLY = ( select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR)
READ_WRITE = (READ_ONLY|select.POLLOUT)
#新建轮询事件对象
poller = select.poll()
#注册本机监听socket到等待可读事件事件集合
poller.register(server,READ_ONLY)
#文件描述符到socket映射
fd_to_socket = {server.fileno():server,}
while True:
print "等待活动连接......"
#轮询注册的事件集合
events = poller.poll(timeout)
if not events:
print "poll超时,无活动连接,重新poll......"
continue
print "有" , len(events), "个新事件,开始处理......"
for fd ,flag in events:
s = fd_to_socket[fd]
#可读事件
if flag & (select.POLLIN | select.POLLPRI) :
if s is server :
#如果socket是监听的server代表有新连接
connection , client_address = s.accept()
print "新连接:" , client_address
connection.setblocking(False) fd_to_socket[connection.fileno()] = connection
#加入到等待读事件集合
poller.register(connection,READ_ONLY)
message_queues[connection] = Queue.Queue()
else :
#接收客户端发送的数据
data = s.recv()
if data:
print "收到数据:" , data , "客户端:" , s.getpeername()
message_queues[s].put(data)
#修改读取到消息的连接到等待写事件集合
poller.modify(s,READ_WRITE)
else :
# Close the connection
print " closing" , s.getpeername()
# Stop listening for input on the connection
poller.unregister(s)
s.close()
del message_queues[s]
#连接关闭事件
elif flag & select.POLLHUP :
print " Closing ", s.getpeername() ,"(HUP)"
poller.unregister(s)
s.close()
#可写事件
elif flag & select.POLLOUT :
try:
msg = message_queues[s].get_nowait()
except Queue.Empty:
print s.getpeername() , " queue empty"
poller.modify(s,READ_ONLY)
else :
print "发送数据:" , data , "客户端:" , s.getpeername()
s.send(msg)
#异常事件
elif flag & select.POLLERR:
print " exception on" , s.getpeername()
poller.unregister(s)
s.close()
del message_queues[s]

Python Epoll Server,基于回调的事件通知模式,轻松管理大量连接:

#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket, select
import Queue serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_address = ("192.168.1.5", 8080)
serversocket.bind(server_address)
serversocket.listen(1)
print "服务器启动成功,监听IP:" , server_address
serversocket.setblocking(0)
timeout = 10
#新建epoll事件对象,后续要监控的事件添加到其中
epoll = select.epoll()
#添加服务器监听fd到等待读事件集合
epoll.register(serversocket.fileno(), select.EPOLLIN)
message_queues = {} fd_to_socket = {serversocket.fileno():serversocket,}
while True:
print "等待活动连接......"
#轮询注册的事件集合
events = epoll.poll(timeout)
if not events:
print "epoll超时无活动连接,重新轮询......"
continue
print "有" , len(events), "个新事件,开始处理......"
for fd, event in events:
socket = fd_to_socket[fd]
#可读事件
if event & select.EPOLLIN:
#如果活动socket为服务器所监听,有新连接
if socket == serversocket:
connection, address = serversocket.accept()
print "新连接:" , address
connection.setblocking(0)
#注册新连接fd到待读事件集合
epoll.register(connection.fileno(), select.EPOLLIN)
fd_to_socket[connection.fileno()] = connection
message_queues[connection] = Queue.Queue()
#否则为客户端发送的数据
else:
data = socket.recv(1024)
if data:
print "收到数据:" , data , "客户端:" , socket.getpeername()
message_queues[socket].put(data)
#修改读取到消息的连接到等待写事件集合
epoll.modify(fd, select.EPOLLOUT)
#可写事件
elif event & select.EPOLLOUT:
try:
msg = message_queues[socket].get_nowait()
except Queue.Empty:
print socket.getpeername() , " queue empty"
epoll.modify(fd, select.EPOLLIN)
else :
print "发送数据:" , data , "客户端:" , socket.getpeername()
socket.send(msg)
#关闭事件
elif event & select.EPOLLHUP:
epoll.unregister(fd)
fd_to_socket[fd].close()
del fd_to_socket[fd]
epoll.unregister(serversocket.fileno())
epoll.close()
serversocket.close()

python--第十天总结(Select/Poll/Epoll使用 )的更多相关文章

  1. python 套接字之select poll epoll

    python下的select模块使用 以及epoll与select.poll的区别 先说epoll与select.poll的区别(总结) select, poll, epoll 都是I/O多路复用的具 ...

  2. Python 协程/异步IO/Select\Poll\Epoll异步IO与事件驱动

    1 Gevent 协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到 ...

  3. python网络编程——IO多路复用select/poll/epoll的使用

    转载博客: http://www.haiyun.me/archives/1056.html http://www.cnblogs.com/coser/archive/2012/01/06/231521 ...

  4. Python自动化 【第十篇】:Python进阶-多进程/协程/事件驱动与Select\Poll\Epoll异步IO

    本节内容: 多进程 协程 事件驱动与Select\Poll\Epoll异步IO   1.  多进程 启动多个进程 进程中启进程 父进程与子进程 进程间通信 不同进程间内存是不共享的,要想实现两个进程间 ...

  5. Python之路-python(Queue队列、进程、Gevent协程、Select\Poll\Epoll异步IO与事件驱动)

    一.进程: 1.语法 2.进程间通讯 3.进程池 二.Gevent协程 三.Select\Poll\Epoll异步IO与事件驱动 一.进程: 1.语法 简单的启动线程语法 def run(name): ...

  6. 转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

    下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...

  7. IO多路复用select/poll/epoll详解以及在Python中的应用

    IO multiplexing(IO多路复用) IO多路复用,有些地方称之为event driven IO(事件驱动IO). 它的好处在于单个进程可以处理多个网络IO请求.select/epoll这两 ...

  8. Python异步非阻塞IO多路复用Select/Poll/Epoll使用,线程,进程,协程

    1.使用select模拟socketserver伪并发处理客户端请求,代码如下: import socket import select sk = socket.socket() sk.bind((' ...

  9. 多进程、协程、事件驱动及select poll epoll

    目录 -多线程使用场景 -多进程 --简单的一个多进程例子 --进程间数据的交互实现方法 ---通过Queues和Pipe可以实现进程间数据的传递,但是不能实现数据的共享 ---Queues ---P ...

  10. Select/Poll/Epoll异步IO

    IO多路复用 同步io和异步io,阻塞io和非阻塞io分别是什么,有什么样的区别? io模式 对于一次io 访问(以read为例),数据会先拷贝到操作系统内核的缓冲区,然后才会从操作系统内核的缓冲区拷 ...

随机推荐

  1. (31)django中的分页器

    book_list = models.Book.objects.all()    #查出指定表中的所有数据paginator = Paginator(book_list,2)    #实例化对象,传入 ...

  2. ubuntu16.04下配置apache2与php

    系统版本:ubuntu16.04 命令均在终端中输入,用浏览器测试 //安装apache2命令 sudo apt install apache2 //测试是否安装成功 浏览器地址栏输入“localho ...

  3. Maya中输出alembic文件的方法

    Maya中输出alembic文件是有现成api调用的,与maya中大部分api一样,这个功能参数的传入是非常类似mel的,本质上讲都是kwargs类型的参数,所以我们传入的参数就需要整理成类似于mel ...

  4. 【python】如何将ipdb的python解释器路径切换至虚拟环境中

    背景: 利用virtualenv构建一个python3.5的虚拟环境,在该虚拟环境中使用ipdb调试程序,结果报错找不到某一个模块. 程序的所有依赖模块都已经成功安装在虚拟环境中. 在虚拟环境中,te ...

  5. sequelize查询数据的日期格式化

    首先确定时区 const sequelize = new Sequelize(config.database, config.username, config.password, { host: co ...

  6. Git-撤销(回退)已经add,commit或push的提交

    本文只阐述如何解决问题,不会对git的各种概念多做介绍,如果有兴趣可以点击下面的链接,进行详细的学习:Pro Git本文适用的环境 现在先假设几个环境,本文将会给出相应的解决方法:1. 本地代码(或文 ...

  7. [UE4]瞬移

    1.设置Input,事件名称设置为Teleport 2.设置事件Teleport 3.

  8. 基于CentOS搭建个人Leanote云笔记本

    Leanote 依赖 MongoDB 作为数据存储,下面开始安装MongoDB: 1. 下载启动 MongoDB 下载 MongoDB 进入 /home 目录,并下载 MongoDB: cd /hom ...

  9. asp.net 微信JsSDK

    有时间再整理吧 using System; using System.Collections.Generic; using System.Linq; using System.Web; using S ...

  10. python——数字问题之_ 变量

    在交互模式中,最后被输出的表达式结果被赋值给变量 _ ._ 变量应被用户视为只读变量 >>> a=12/2.3 >>> b=1.2 >>> a*b ...