SocketServer
socket编程过于底层,编程虽然有套路,但是想要写出健壮的代码还是比较困难的,所以很多语言都对socket底层API进行封装,Python的封装就是——socketserver模块。它是网络服务编程框架,便于企业级快速开发。
SocketServer简化了网络服务器的编写。
它有4个同步类:TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。
2个Mixin类:ForkingMixIn 和 ThreadingMixIn 类,用来支持异步。
class ForkingUDPServer(ForkingMixIn, UDPServer): pass
class ForkingTCPServer(ForkingMixIn, TCPServer): pass
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
fork是创建多进程,thread是创建多线程
编程接口
socketserver.BaseServer(server_address, RequestHandlerClass)
需要提供服务器绑定的地址信息,和用于处理请求的RequestHandlerClass类。
RequestHandlerClass类必须是BaseRequestHandler类的子类。
BaseRequestHandler类
它是和用户连接的用户请求处理类的基类,定义为BaseRequestHandler(request, client_address, server)
服务端Server实例接收用户请求后,最后会实例化这个类。
它被初始化时,送入3个构造参数:request, client_address, server自身
以后就可以在BaseRequestHandler类的实例上使用以下属性:
self.request是和客户端的连接的socket对象
self.server是TCPServer实例本身
self.client_address是客户端地址
这个类在初始化的时候,它会依次调用3个方法。子类可以覆盖这些方法。
# BaseRequestHandler要子类覆盖的方法 class BaseRequestHandler: def __init__(self, request, client_address, server): self.request = request self.client_address = client_address self.server = server self.setup() try: self.handle() finally: self.finish() def setup(self): # 每一个连接初始化 pass def handle(self): # 每一次请求处理 pass def finish(self): # 每一个连接清理 pass
ThreadingTCPServer是异步的,可以同时处理多个连接。
TCPServer是同步的,一个连接处理完了,即一个连接的handle方法执行完了,才能处理另一个连接,且只有主线程。
ThreadingTCPServer实现的群聊服务器

import socketserver import threading import logging import sys FORMAT = "%(asctime)s %(thread)s %(message)s" logging.basicConfig(format=FORMAT, level=logging.INFO) class ChatHandle(socketserver.BaseRequestHandler): clients = {} # 记录客户端 def setup(self): super().setup() self.even = threading.Event() self.clients[self.client_address] = self.request # 添加客户端 def finish(self): super().finish() self.even.set() self.clients.pop(self.client_address) # 删除客户端 def handle(self): super().handle() logging.info("{1}:{0}".format(self.request, self.client_address)) while not self.even.is_set(): try: data = self.request.recv(1024) except Exception: break if data.strip() == b"quit": break msg = "{}{}".format(self.client_address, data.decode()) logging.info(msg) msg = msg.encode() for f in self.clients.values(): # 发送给各个客户端 f.send(msg) print("end") def main(): # 建立服务器 server = socketserver.ThreadingTCPServer(("127.0.0.1", 9999), ChatHandle) threading.Thread(target=server.serve_forever, name="server").start() try: while True: cmd = input(">>>") if cmd.strip() == "quit": server.shutdown() break print(threading.enumerate()) except Exception as e: print(e) except KeyboardInterrupt: pass finally: print("exit") sys.exit(0) if __name__ == '__main__': main()
创建服务器需要几个步骤:
1. 从BaseRequestHandler类派生出子类,并覆盖其handle()方法来创建请求处理程序类,此方法将处理传入请求
2. 实例化一个服务器类,传参服务器的地址和请求处理类
3. 调用服务器实例的handle_request()或serve_forever()方法
4. 调用server_close()关闭套接字