事件驱动I/O本质上来讲就是将基本I/O操作(比如读和写)转化为你程序需要处理的事件。 例如当数据在某个socket上被接受后它会转换成一个 receive 事件然后被你定义的回调方法或函数来处理。 作为一个可能的起始点一个事件驱动的框架可能会以一个实现了一系列基本事件处理器方法的基类开始
class EventHandler:
def fileno(self):
Return the associated file descriptor
raise NotImplemented(must implement)
def wants_to_receive(self):
Return True if receiving is allowed
return False
def handle_receive(self):
Perform the receive operation
pass
def wants_to_send(self):
Return True if sending is requested
return False
def handle_send(self):
Send outgoing data
pass
这个类的实例作为插件被放入类似下面这样的事件循环中
import select
def event_loop(handlers):
while True:
wants_recv [h for h in handlers if h.wants_to_receive()]
wants_send [h for h in handlers if h.wants_to_send()]
can_recv, can_send, _ select.select(wants_recv, wants_send, [])
for h in can_recv:
h.handle_receive()
for h in can_send:
h.handle_send()
事件循环的关键部分是 select() 调用它会不断轮询文件描述符从而激活它。 在调用 select() 之前事件循环会询问所有的处理器来决定哪一个想接受或发生。 然后它将结果列表提供给 select() 。然后 select() 返回准备接受或发送的对象组成的列表。 然后相应的 handle_receive() 或 handle_send() 方法被触发。
编写应用程序的时候EventHandler 的实例会被创建。例如下面是两个简单的基于UDP网络服务的处理器例子
import socket
import time
class UDPServer(EventHandler):
def __init__(self, address):
self.sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.bind(address)
def fileno(self):
return self.sock.fileno()
def wants_to_receive(self):
return True
class UDPTimeServer(UDPServer):
def handle_receive(self):
msg, addr self.sock.recvfrom(1)
self.sock.sendto(time.ctime().encode(ascii), addr)
class UDPEchoServer(UDPServer):
def handle_receive(self):
msg, addr self.sock.recvfrom(8192)
self.sock.sendto(msg, addr)
if __name__ __main__:
handlers [ UDPTimeServer((,14000)), UDPEchoServer((,15000)) ]
event_loop(handlers)
测试这段代码试着从另外一个Python解释器连接它
>>> from socket import *
>>> s socket(AF_INET, SOCK_DGRAM)
>>> s.sendto(b,(localhost,14000))
0
>>> s.recvfrom(128)
(bTue Sep 18 14:29:23 2012, (127.0.0.1, 14000))
>>> s.sendto(bHello,(localhost,15000))
5
>>> s.recvfrom(128)
(bHello, (127.0.0.1, 15000))
>>>
实现一个TCP服务器会更加复杂一点因为每一个客户端都要初始化一个新的处理器对象。 下面是一个TCP应答客户端例子
class TCPServer(EventHandler):
def __init__(self, address, client_handler, handler_list):
self.sock socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
self.sock.bind(address)
self.sock.listen(1)
self.client_handler client_handler
self.handler_list handler_list
def fileno(self):
return self.sock.fileno()
def wants_to_receive(self):
return True
def handle_receive(self):
client, addr self.sock.accept()
# Add the client to the event loops handler list
self.handler_list.append(self.client_handler(client, self.handler_list))
class TCPClient(EventHandler):
def __init__(self, sock, handler_list):
self.sock sock
self.handler_list handler_list
self.outgoing bytearray()
def fileno(self):
return self.sock.fileno()
def close(self):
self.sock.close()
# Remove myself from the event loops handler list
self.handler_list.remove(self)
def wants_to_send(self):
return True if self.outgoing else False
def handle_send(self):
nsent self.sock.send(self.outgoing)
self.outgoing self.outgoing[nsent:]
class TCPEchoClient(TCPClient):
def wants_to_receive(self):
return True
def handle_receive(self):
data self.sock.recv(8192)
if not data:
self.close()
else:
self.outgoing.extend(data)
if __name__ __main__:
handlers []
handlers.append(TCPServer((,16000), TCPEchoClient, handlers))
event_loop(handlers)
TCP例子的关键点是从处理器中列表增加和删除客户端的操作。 对每一个连接一个新的处理器被创建并加到列表中。当连接被关闭后每个客户端负责将其从列表中删除。 如果你运行程序并试着用Telnet或类似工具连接它会将你发送的消息回显给你。并且它能很轻松的处理多客户端连接。