socketserver源码分析
socketserver用于建立服务端,讨论TCPServer,一步步简化代码,分析源码
建立连接
- __init__,server_bind,server_activate对应打开套接字,绑定ip和port,监听端口
关闭连接
- handle_request处理一段请求请求后,关闭连接
- serve_forever启动监听,采用多路复用提高吞吐量,shutdown用来关闭连接;serve_forever阻断当前线程,监听selector,shutdown必须在另一个线程设置__shutdown_request,两个线程采用threading.Event通信
import service as socketserver
from concurrent.futures import ThreadPoolExecutor
import time
def run(socket):
print('run begin')
socket.serve_forever(5)
print('run end')
def shutdown(socket):
print('shutdown begin')
time.sleep(1 * 60)
print('shutdown exec')
socket.shutdown()
print('shutdown end')
def main():
socket = socketserver.TCPServer(('0.0.0.0', 8081), socketserver.BaseRequestHandler)
with ThreadPoolExecutor(4) as pool:
pool.submit(run, socket)
pool.submit(shutdown, socket)
if __name__ == '__main__':
main()
# 输出
# run begin
# shutdown begin
# shutdown exec
# run end
# shutdown end
处理请求
- 从selector接受到信号后,调用_handle_request_noblock处理连接请求
- _handle_request_noblock调用get_request(连接可能是tcp或udp)获取请求数据
- _handle_request_noblock调用process_request实际处理请求,失败时调用handle_error,shutdown_request关闭请求
处理请求详细
- process_request先调用finish_request正常处理请求后,再调用shutdown_request关闭请求
- finish_request调用创建时RequestHandlerClass类,创建实例处理请求,大多设计都将处理连接和处理请求数据分开到两个类,解耦
- process_request可以放在线程或进程中处理提高并发处理
RequestHandler
- BaseRequestHandler流程很清晰在构造中__init__中执行setup,handle,finish,执行完即销毁
- 默认提供BaseRequestHandler及子类的handle没有处理过程,自己必须实现
- 实现一个简单http请求返回demo
# 服务端
import service as socketserver
class HttpRequestHandle(socketserver.StreamRequestHandler):
def handle(self):
data = self.request.recv(1024)
if data:
print(data)
data += b'ok'
resp = b'HTTP/1.1 200 OK\r\ncontent-type: application/json\r\ndate: Wed, 28 Apr 2021 16:20:04 GMT\r\nserver: socketserver\r\n\r\n{"code":"0000","message":"ok","data":{}}'
self.request.send(resp)
def service_demo():
print('service begin')
socket = socketserver.TCPServer(('0.0.0.0', 8081), HttpRequestHandle)
socket.serve_forever(1)
print('service end')
if __name__ == '__main__':
service_demo()
# 客户端
import http.client
import json
def client_demo():
client = http.client.HTTPConnection('127.0.0.1', 8081)
headers = {'test_head': 'nxx'}
client.request('GET', '/index.html', headers=headers)
respnese = client.getresponse()
print(json.loads(respnese.read()))
if __name__ == '__main__':
client_demo()
# 服务端输出
# service begin
# b'GET /index.html HTTP/1.1\r\nHost: 127.0.0.1:8081\r\nAccept-Encoding: identity\r\ntest_head: nxx\r\n\r\n'
# 客户端输出
# {'code': '0000', 'message': 'ok', 'data': {}}
StreamRequestHandler
- StreamRequestHandler默认将接受和发送关联到rfile,wfile,可以像读写文件一样处理请求
# 服务端
data = self.rfile.readline()
while data and data != b'\r\n':
print(data)
data = self.rfile.readline()
resp = b'HTTP/1.1 200 OK\r\ncontent-type: application/json\r\ndate: Wed, 28 Apr 2021 16:20:04 GMT\r\nserver: socketserver\r\n\r\n{"code":"0000","message":"ok","data":{}}'
self.wfile.write(resp)