21、第七 - 网络编程基础 - IO多路复用之selectors模块(单线程支持上万并发)
Python中的selectors模块,是允许在选择模块原语上建立高级和高效的I/O复用。根据平台选出最佳的IO多路机制,比如在win的系统上他默认的是select模式;而在linux上它默认的epoll。三种IO多路机制都支持:select\poll\epoll。
功能与linux的epoll,还是select模块,poll等类似;实现高效的I/O multiplexing, 常用于非阻塞的socket的编程中; 简单介绍一下这个模块,更多内容查看 python文档:https://docs.python.org/3/library/selectors.html
1. 模块定义了一个 BaseSelector的抽象基类, 以及它的子类,包括:
- SelectSelector
- PollSelector
- EpollSelector
- DevpollSelector
- KqueueSelector
另外还有一个DefaultSelector类,它其实是以上其中一个子类的别名而已,它自动选择为当前环境中最有效的Selector,所以平时用。
2. 模块定义了两个常量,用于描述 event Mask
- EVENT_READ : 表示可读的; 它的值其实是1;
- EVENT_WRITE: 表示可写的; 它的值其实是2;
3. 模块定义了一个 SelectorKey类, 一般用这个类的实例 来描述一个已经注册的文件对象的状态, 这个类的几个属性常用到:
- fileobj: 表示已经注册的文件对象;
- fd: 表示文件对象的描述符,是一个整数,它是文件对象的 fileno()方法的返回值;
- events: 表示注册一个文件对象时,我们等待的events, 即上面的event Mask, 是可读呢还是可写呢!!
- data: 表示注册一个文件对象是邦定的data;
4. 说一下抽象基类中的方法:
register(fileobj, events, data=None) 作用:注册一个文件对象。 参数: fileobj——即可以是fd 也可以是一个拥有fileno()方法的对象; events——上面的event Mask 常量; data返回值: 一个SelectorKey类的实例; unregister(fileobj) 作用: 注销一个已经注册过的文件对象; 返回值:一个SelectorKey类的实例; modify(fileobj, events, data=None) 作用:用于修改一个注册过的文件对象,比如从监听可读变为监听可写;它其实就是register() 后再跟unregister(), 但是使用modify( ) 更高效; 返回值:一个SelectorKey类的实例; select(timeout=None) 作用: 用于选择满足我们监听的event的文件对象; 返回值: 是一个(key, events)的元组, 其中key是一个SelectorKey类的实例, 而events 就是 event Mask(EVENT_READ或EVENT_WRITE,或者二者的组合) close() 作用:关闭 selector。 最后一定要记得调用它, 要确保所有的资源被释放; get_key(fileobj) 作用: 返回注册文件对象的 key; 返回值 :一个SelectorKey类的实例
举例:
服务端:
import selectors import socket class Server(object): def __init__(self,sel,sock): self.sel = sel self.sock = sock def run(self,host,port): self.sock.bind((host,port)) self.sock.listen(1000) self.sock.setblocking(False) self.sel.register(self.sock,selectors.EVENT_READ,self.accept) while True: events = self.sel.select() #默认阻塞,有活动连接就返回活动连接列表 print(events) for key,mask in events: callback = key.data callback(key.fileobj,mask) def accept(self,sock,mask): conn,addr = sock.accept() print("acceptd",conn,"from",addr) conn.setblocking(False) sel.register(conn,selectors.EVENT_READ,self.read) def read(self,conn,mask): data = conn.recv(1024) if data: print("echo",repr(data),"to",conn) conn.send(data) else: print("closing",conn) sel.unregister(conn) conn.colse() if __name__ == "__main__": sel = selectors.DefaultSelector() sock = socket.socket() host,port = 'localhost',10006 server_obj = Server(sel,sock) server_obj.run(host,port)
客户端:
import socket client = socket.socket() client.connect(("localhost",10006)) while True: msg = bytes(input(">>:").strip(),encoding="utf-8") if len(msg) == "q".encode("utf-8"): exit("退出!") client.sendall(msg) data = client.recv(1024) print("recv:", data.decode()) client.close()
人有傲骨终不贱,脚踏实地见真章;
超出预期为工作,价值体现显能力。