python-cookbook事件驱动的i/o模型
来源:互联网 发布:淘宝hd版怎么不了图片 编辑:程序博客网 时间:2024/05/21 04:43
行行色色的事件驱动或异步I/O的包, 我们无法理解底层实现模型, 根据python的cookbook, 比较明了的体现了该模型, 便于今后理解及开发相关ioloop, eventloop。
基于select实现的单进程单线程的事件轮询模型。 测试用telnet 127.0.0.1 8888可以多开几个窗口
#!/usr/bin/env pythonclass EventHandler(object): """An I/O event loop """ def fileno(self): raise NotImplemented 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 data """ passimport socketclass 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): # import time # time.sleep(3) client, addr = self.sock.accept() # Add the client to the event loop's handler list self.handler_list.append(self.client_handler(client, self.handler_list)) print "accept:%s,%s" % (client, addr) 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() 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) print "recv:%s" % data if not data: self.close() else: self.outgoing.extend(data)import selectdef 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, err = select.select(wants_recv, wants_send, []) for h in can_recv: h.handle_receive() for h in can_send: h.handle_send()if __name__ == "__main__": handlers = [] handlers.append(TCPServer(('', 8888), TCPEchoClient, handlers)) event_loop(handlers)
附加:
服务端:
sock.bind(addr)sock.listen(1) # servclient,addr=sock.accept() client.recv/send客服端:
sock.connect(addr)scok.recv/send
1. serv只有一个, 可以不停的accept
2. 一次主动connect, 建立一个连接. accept与connect对应
3. serv的端口与ip是bind不变的
4. 每次connect之后,会分配一个新端口与之通信(新的socket的fd)
5. event loop模型就是处理一个serv不停的accept, 多个client的读写问题。很多技术贴解释select, epool说可读可写意思是:
可读: accept到了一个client
可写: client读完了(if not data: client.send(self.outgoing)), 不考虑没recv完send情况,其中细节有很多
流程描述:
第一次遍历handlers:
可读: accept一个client之后, 取名叫client0, client0加入handers(client0此时是可读不可写)。
可写队列空
第二次遍历handlers:
可读: 发现client0可读, 执行handle_receive(读取数据到outgoing)。如果又新accpet一个client, 取名叫client1, 执行handler_receive加入handlers, client1可读不可写
可写: 空
第三次遍历handlers:
可读: client0可读, 执行handle_receive, 假如这里已经读取完了所有数据(这里有一个读完了的细节), 从handlers里面移除. client1与client0的第二次遍历一样
可写: client0可写, send完数据. 如果需多次send, 需多次select(每次select返回该client可写)
简单来说:
serv可读
client可读
client可写
可读, 可写多次
移除client
阻塞, 或继续serv可读
一次connect至少需要三次select返回
UDP的event loop实例,来自cookbook
很方便处理多个udp连接
#!/usr/bin/env pythonclass EventHandler(object): """An I/O event loop """ def fileno(self): raise NotImplemented 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 data """ passimport socketimport timeclass UDPServer(EventHandler): """An UDP io event loop handler """ 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): # blocking return True class UDPTimeServer(UDPServer): def handle_receive(self): msg, addr = self.sock.recvfrom(1) self.sock.sendto(str(time.time()).encode('ascii'), addr)class UDPEchoServer(UDPServer): def handle_receive(self): msg, addr = self.sock.recvfrom(1024 * 10) self.sock.sendto(msg, addr)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, err = select.select(wants_recv, wants_send, []) for h in can_recv: h.handle_receive() for h in can_send: h.handle_send()if __name__: handlers = [UDPTimeServer(('', 14000)), UDPEchoServer(('', 15000))] event_loop(handlers)
测试
import socketimport timesock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)sock2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)sock.sendto(b'',('localhost',14000))print(sock.recvfrom(1024))sock2.sendto(b'hi', ('',15000))print(sock.recvfrom(1024)) print(sock2.recvfrom(1024))~
- python-cookbook事件驱动的i/o模型
- 事件驱动I/O模型
- Linux I/O模型 与 Java I/O模型、驱动中的异步函数 的区别。
- Winsock的事件I/O异步模型——WSAEventSelect
- 基于事件I/O模型的socket通信
- 事件通知方式实现的重叠I/O模型
- 基于事件通知的重叠I/O网络模型
- linux信号驱动I/O模型服务器
- nodejs 异步I/O和事件驱动
- NodeJS中的异步I/O、事件驱动
- python的I/O
- socket的I/O模型
- I/O模型的区别
- LINUX的I/O模型
- python 事件驱动编程模型
- Socket I/O模型之重叠I/O(overlapped I/O)--事件通知
- socket通信之六:Overlapped I/O 事件通知模型实现的客户/服务器模型
- Linux服务器--服务器模型,I/O模型,两种高效的事件处理模式
- Android自学网站/博客/微信公众号
- Maven项目错误解决小结
- 关于底部虚拟按钮
- 我的Linux书架
- 关于程序不能启动,报告0x0...7B错误
- python-cookbook事件驱动的i/o模型
- 读书笔记-现代操作系统-3储存管理-3.5分页系统中的设计问题
- PAT乙级练习题B1050. 螺旋矩阵
- web设计之:崛起中的九大HTML5开发工具
- Java并发编程:深入剖析ThreadLocal
- 在Pycharm中创建Django开发环境
- 使用Eclipse从数据库逆向生成实体类
- 深圳同城快跑 上机试题02:从文本中读取内容,并统计不同英语单词出现的次数,最后输出到新的文本文件
- 【BZOJ1977】【BJOI2011】严格次小生成树