TCP服务端
1. 前言
在TCP通信章节中,我们介绍了使用socket
模块创建TCP服务端的方法。但其实,Python的系统库中也提供了一个TCPServer
的实现,这个模块就是SocketServer
,在Python3中,该模块改成了socketserver
。
可以使用以下代码进行兼容:
try:
import socketserver
except ImportError:
import SocketServer as socketserver
2. 创建请求处理器
TCPServer
类的请求参数,需要传入一个BaseRequestHandler
子类作为请求处理器。socketserver模块中实现了两个处理器:StreamRequestHandler
和DatagramRequestHandler
,分别对应TCP协议和UDP协议。
下面看一个简单的例子:
class EchoRequestHandler(socketserver.StreamRequestHandler):
'''Echo请求处理器
'''
def handle(self):
print('Recv new connection...')
buff = b''
while True:
c = self.rfile.read(1)
buff += c
if c == b'\n':
self.wfile.write(buff)
buff = b''
这个例子实现了一个Echo服务器,当用户输入一句话,并发送回车后,Server会将这句话返回给用户。
3. 启动TCP服务器
server = socketserver.TCPServer(('127.0.0.1', 7777), EchoRequestHandler)
server.serve_forever()
启动TCP服务器后,请求可以被正常处理,但是有个问题:只能接受一个连接。这是因为我们使用了一个死循环,而TCPServer本身是单线程的。
其实,只要将TCPServer
替换为ThreadingTCPServer
就可以了。此时,每个请求会使用一个线程处理,因此不会阻塞请求。
server = socketserver.ThreadingTCPServer(('127.0.0.1', 7777), EchoRequestHandler)
server.serve_forever()
但是ThreadingTCPServer
并不适合高并发场景,因为线程的总数是有限的,随着线程数量的增加,性能可能会不断下降。
4. ThreadingTCPServer的实现分析
class ThreadingMixIn:
"""Mix-in class to handle each request in a new thread."""
# Decides how threads will act upon termination of the
# main process
daemon_threads = False
def process_request_thread(self, request, client_address):
"""Same as in BaseServer but as a thread.
In addition, exception handling is done here.
"""
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
def process_request(self, request, client_address):
"""Start a new thread to process the request."""
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
t.start()
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
这里使用了mix-in
方式,ThreadingMixIn
类重写了process_request
方法,因此继承自ThreadingMixIn
类的子类也重写了这个方法,从而做到对每个请求使用单独的线程处理。