selectors 是 Python 3.4 引入的一种高级 I/O 多路复用模块。

它选择系统中性能最好的方式。查看 selectors.py 源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Choose the best implementation, roughly:
# epoll|kqueue|devpoll > poll > select.
# select() also can't accept a FD > FD_SETSIZE (usually around 1024)
if 'KqueueSelector' in globals():
DefaultSelector = KqueueSelector
elif 'EpollSelector' in globals():
DefaultSelector = EpollSelector
elif 'DevpollSelector' in globals():
DefaultSelector = DevpollSelector
elif 'PollSelector' in globals():
DefaultSelector = PollSelector
else:
DefaultSelector = SelectSelector

它在 Linux 系统中会优先选择 epoll 方式(kqueue, devpoll 分别在 FreeBSD, Solarios 使用)。

使用 selectors 模块,避免了底层调用,也更方便。

这里可以查看 Python 官方文档的例子,也是 echo server 。同上一篇文章一样,你同样可以使用 telnet 来访问它(注意端口是 1234)。

抄录如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import selectors
import socket

sel = selectors.DefaultSelector()

def accept(sock, mask):
conn, addr = sock.accept() # Should be ready
print('accepted', conn, 'from', addr)
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read)

def read(conn, mask):
data = conn.recv(1000) # Should be ready
if data:
print('echoing', repr(data), 'to', conn)
conn.send(data) # Hope it won't block
else:
print('closing', conn)
sel.unregister(conn)
conn.close()

sock = socket.socket()
sock.bind(('localhost', 1234))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)

while True:
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)