官方文档见:https://docs.python.org/3/library/selectors.html

  selectors模块  它封装了IO多路复用中的select和epoll,能够更快,更方便的实现多并发效果。

1. 模块定义了一个 BaseSelector的抽象基类, 以及它的子类,包括:SelectSelector, PollSelector, EpollSelector, DevpollSelector, KqueueSelector.    

另外还有一个DefaultSelector类,它其实是以上其中一个子类的别名而已,它自动选择为当前环境中最有效的Selector,所以平时用 DefaultSelector类就可以了,其它用不着。

2. 模块定义了两个常量,用于描述 event Mask

EVENT_READ :      表示可读的; 它的值其实是1;

EVENT_WRITE:      表示可写的; 它的值其实是2;

3. 模块定义了一个 SelectorKey类, 一般用这个类的实例 来描述一个已经注册的文件对象的状态, 这个类的几个属性常用到:

fileobj:   表示已经注册的文件对象;

fd:          表示文件对象的描述符,是一个整数,它是文件对象的 fileno()方法的返回值;

events:    表示注册一个文件对象时,我们等待的events, 即上面的event Mask, 是可读还是可写呢!!

data:       表示注册一个文件对象是邦定的data;

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类的实例;

实例:

server.py

1 import selectors
 2 import socket
 3 
 4 # selectors模块默认会用epoll,如果你的系统中没有epoll(比如windows)则会自动使用select
 5 sel = selectors.DefaultSelector()  # 生成一个select对象
 6 
 7 
 8 def accept(sock, mask):
 9     conn, addr = sock.accept()  # Should be ready
10     print('accepted', conn, 'from', addr)
11     conn.setblocking(False)  # 设定非阻塞
12     sel.register(conn, selectors.EVENT_READ, read)  # 新连接注册read回调函数
13 
14 
15 def read(conn, mask):
16     try:
17         data = conn.recv(1024)  # Should be ready
18         if not data:
19             raise  Exception
20         print(str(data.decode("utf-8")))
21         conn.send(data)
22     except Exception as e:
23         print('closing', conn)
24         sel.unregister(conn)
25         conn.close()
26 
27 sock = socket.socket()
28 sock.bind(('127.0.0.1', 8899))
29 sock.listen()
30 sock.setblocking(False)
31 sel.register(sock, selectors.EVENT_READ, accept)  # 把刚生成的sock连接对象注册到select连接列表中,并交给accept函数处理
32 
33 while True:
34     print("server-------")
35     events = sel.select()  # 默认是阻塞,有活动连接就返回活动的连接列表
36     # 这里看起来是select,其实有可能会使用epoll,如果你的系统支持epoll,那么默认就是epoll
37     # print("event==",events)
38     for key, mask in events:
39         # print("key==",key)
40         # print("mask==",mask)
41         callback = key.data  # 去调accept函数
42         # print("callback==",callback)
43         callback(key.fileobj, mask)  # key.fileobj就是readable中的一个socket连接对象

client.py

1 import socket
 2 
 3 sk = socket.socket()
 4 sk.connect(("127.0.0.1", 8899))
 5 
 6 while True:
 7     inp = input(">>>:")
 8     sk.send(inp.encode("utf-8"))  # 发送信息
 9     data = sk.recv(1024)  # 接受信息
10     print(data.decode("utf-8"))  # 打印出来client2.py