1.
它的功能与 linux 的 epoll,还是 select 模块, poll 等类似;实现高效的 I/O multiplexing , 常用于非阻塞的 socket 的编程中。
1.1 内部类
模块定义了一个 BaseSelector的抽象基类, 以及它的子类,包括:SelectSelector, PollSelector, EpollSelector, DevpollSelector, KqueueSelector.
另外还有一个DefaultSelector类,它其实是以上其中一个子类的别名而已,它自动选择为当前环境中最有效的Selector,所以平时用 DefaultSelector类就可以了,其它用不着。
1.2 模块定义了两个常量,用于描述 event Mask
- EVENT_READ : 表示可读的; 它的值其实是1;
- EVENT_WRITE: 表示可写的; 它的值其实是2;
1.3 模块定义了一个 SelectorKey类, 一般用这个类的实例 来描述一个已经注册的文件对象的状态, 这个类的几个属性常用到
- fileobj: 表示已经注册的文件对象
- fd: 表示文件对象的描述符,是一个整数,它是文件对象的 fileno()方法的返回值;
- events: 表示注册一个文件对象时,我们等待的events, 即上面的event Mask, 是可读呢还是可写呢!!
- data: 表示注册一个文件对象是邦定的data;
1.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类的实例;
1.5 实例
# selector_server.py
import selectors
import socket
# selectors模块默认会用epoll,如果你的系统中没有epoll(比如windows)则会自动使用select
sel = selectors.DefaultSelector() # 生成一个select对象
def accept(sock, mask):
conn, addr = sock.accept() # Should be ready
print('accepted', conn, 'from', addr) # accepted <socket.socket fd=640, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.10.248', 9999), raddr=('192.168.10.248', 56917)> from ('192.168.10.248', 56917)
conn.setblocking(False) # 设定非阻塞
sel.register(conn, selectors.EVENT_READ, read) # 新连接注册read回调函数
def read(conn, mask):
data = conn.recv(1024) # Should be ready
if data:
print('echoing', repr(data), 'to', conn)
conn.send(data)
else:
print('closing', conn)
sel.unregister(conn)
conn.close()
sock = socket.socket()
host = socket.gethostname()
sock.bind((host, 9999))
sock.listen()
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept) # 把刚生成的sock连接对象注册到select连接列表中,并交给accept函数处理
while True:
events = sel.select() # 默认是阻塞,有活动连接就返回活动的连接列表
# 这里看起来是select,其实有可能会使用epoll,如果你的系统支持epoll,那么默认就是epoll
for key, mask in events:
print(key.data) # <function accept at 0x00000255221AD708>
print(key.fileobj) # <socket.socket fd=472, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.10.248', 9999)>
print(mask) # 1
callback = key.data # 注册的回调函数
callback(key.fileobj, mask) # key.fileobj就是readable中的一个socket连接对象,运行回调函数。