Python中的selector模块(selectors)为网络编程提供了非常方便的异步I/O解决方案,可以用来解决I/O操作的阻塞问题。
什么是Python多路复用selector模块?
在Python的标准库中,有一个selectors模块(在Python3中),这个模块提供了一种用于多路复用的支持,能够基于select、epoll、kqueue等系统调用实现I/O多路复用。
selector可以同时监控多个socket的可读、可写、错误状态,并且比起其他的多路复用模块来说更易于使用和控制。selectors在事件驱动模型中起到了非常重要的作用。
如何使用Python多路复用selector模块?
下面演示一下简单的使用控制台来多路复用监控多个socket的连接状态(读/写/异常)。
创建selector对象
创建selector对象之前,我们需要确认当前操作系统支持的多路复用模型,具体的模型与系统有关,常见的包括select、epoll、kqueue等模型。在Python标准库selectors中,可以根据需要自动选择具体的模型,这样就能够减轻可移植性的担忧。
import selectors
sel = selectors.DefaultSelector()
注册和取消注册事件
我们也可以将之前建立连接的socket对象跟selector对象绑定起来,注册事件监控。
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('example.com', 80))
sel.register(sock, selectors.EVENT_READ, data=None)
绑定事件就是将socket对象、事件类型、附加数据一起封装成Event对象,通过register()方法将Event对象提交给selector对象。
需要注意的是,如果一个socket对象已经被注册,又希望修改对这个socket对象监听的事件,则需要先使用 unregister() 方法将之前的绑定事件取消。
sel.unregister(sock)
开始事件循环
接下来,我们需要遍历从select()方法返回的所有事件(Event)并根据事件类型执行不同的操作。
while True:
events = sel.select()
for key, mask in events:
sock = key.fileobj
if mask & selectors.EVENT_READ:
# data = sock.recv()
pass
if mask & selectors.EVENT_WRITE:
# sock.send(data)
pass
示例1:多路复用监控http请求
以下示例代码演示了如何使用selector多路复用监控http请求:
import selectors
import socket
host = 'www.baidu.com'
port = 80
data = "GET / HTTP/1.1\r\nHost: %s\r\n\r\n" % host
sel = selectors.DefaultSelector()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(False)
sock.connect((host, port))
events = selectors.EVENT_READ | selectors.EVENT_WRITE
sel.register(sock, events)
try:
while True:
events = sel.select(timeout=1)
if not events:
continue
for key, mask in events:
sock = key.fileobj
if mask & selectors.EVENT_WRITE:
sent = sock.send(data.encode('utf-8'))
sel.modify(sock, selectors.EVENT_READ)
elif mask & selectors.EVENT_READ:
received = sock.recv(1024)
if received:
print(received.decode('utf-8'))
sel.close()
break
break
finally:
sel.close()
示例2:监控多个连接并同时处理请求
以下示例代码演示了如何使用selector同时监控多个socket连接的状态,并实现同时处理请求:
import selectors
import socket
host = '127.0.0.1'
port = 9090
sel = selectors.DefaultSelector()
def accept(sock):
conn, addr = sock.accept()
print('connected to:', addr)
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read)
def read(conn):
data = conn.recv(1024)
if not data:
sel.unregister(conn)
conn.close()
return
print(data.decode('utf-8'))
conn.sendall(data)
sock = socket.socket()
sock.bind((host, port))
sock.listen(5)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
while True:
event_list = sel.select()
for key, mask in event_list:
callback = key.data
sock = key.fileobj
callback(sock)
总结
通过使用selector模块,我们可以实现控制对多个socket连接的状态,以及实现回调函数的功能,将所有的socket对象汇集到一个事件循环中,通过select()系统调用来等待数据到来,以实现非阻塞的I/O多路复用。这样就可以大大提高网络编程效率,并且利用callback函数和当期Python版本的asyncore模块,也可以实现高效的异步I/O编程。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Python多路复用selector模块的基本使用 - Python技术站