高效的原因
1.是epoll是os层面操作,且封装成事件,监听,回调处理。
2. 把程序单位封装成更小的可异步调用
socketserver
socketserver是socket的高级版,也是是基于epoll这样的方案,且
封装了ThreadingUDPServer, ForkUDPServer等各种高性能接口。
epoll 说明
Python标准库提供的selectors模块是对底层select/poll/epoll/kqueue的封装。DefaultSelector类会根据 OS 环境自动选择最佳的模块,那在 Linux 2.5.44 及更新的版本上都是epoll了。
socket服务端低级写法
import socket
HOST = ''127.0.0.1"
PORT = 50007
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024)
if not data: break
conn.sendall(data)
客户端接收:
import socket
HOST = '127.0.0.1'
PORT = 50007
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((HOST, PORT))
s.sendall(b'Hello, world')
data = s.recv(1024)
print('Received', repr(data))
可以发现socket的服务端,不管有没有数据,一直开着,十分浪费资源,基于线程处理,效率也不高。
io复用epoll的高性能socket服务端
import selectors
import socket
selector = selectors.DefaultSelector()
stopped = False
def accept(sock, mask):
conn, addr = sock.accept()
conn.setblocking(False)
selector.register(conn, selectors.EVENT_READ, read)
print(1111)
def read(conn, mask):
data = conn.recv(1000)
if data:
print(22222)
print(data)
conn.send(data)
else:
selector.unregister(conn)
conn.close()
sock =socket.socket()
sock.bind(("127.0.0.1", 60001))
sock.listen(100)
selector.register(sock, selectors.EVENT_READ, accept)
while 1:
events = selector.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)
客户端接收
import socket
sock = socket.socket()
sock.connect(("127.0.0.1", 60001))
sock.send(b"doffda")
测试结果
写得不错的:
http://www.jianshu.com/writer#/notebooks/3402008/notes/24354397
http://usyiyi.cn/translate/python_352/library/selectors.html