import threading from socketserver import ThreadingTCPServer,BaseRequestHandler import sys import logging FORMAT = '%(asctime)s %(threadName)d %(thread)d %(message)s' logging.basicConfig(level=logging.INFO,format=FORMAT) class ChatHandler(BaseRequestHandler): #一对一,相当于receive函数 clients = {} def setup(self): self.event = threading.Event() self.clients[self.client_address] = self.request def finish(self): self.clients.pop(self.client_address) self.event.set() def handle(self): print(self.request) #new_socket 用来recv while not self.event.is_set(): data = self.request.recv(1024) print(data,self.client_address) msg = '{}.ack'.format(data).encode() #如何实现一对多,多在哪里,如何获得 for c in self.clients.values(): c.send(msg) addr = ('0.0.0.0',9999) server = ThreadingTCPServer(addr,ChatHandler) #相当于tcp总的socket print(server) t = threading.Thread(target=server.serve_forever,name='chatserver') t.start() try: while True: cmd = input(">>>") if cmd.strip() == 'quit': break except Exception as e: print(e) except KeyboardInterrupt: pass finally: print('exit') sys.exit(0)
类的继承关系
BaseServer------>TCPServer---------->UDPServer | | UnixStreamServer UnixDatagramServer
socketserver有四个同步类 TCPServer,UDPAServer,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是创建多线程
BaseRequestHandler类 它是用户连接的用户请求处理类的基类,定义为BaseRequestHandler(request,client_address,server) 服务端server实例接收用户请求后,最后会实例化这个类 它被实例化时,送入3个构建参数,request,client,address,server自身 以后就可以在BaseRequestHandler类的实例上使用一下属性 self.request是和客户端连接的socket对象 self.server是tcpserver本身 self.client_address是客户端的地址 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
官方文档例子
import socketserver class MyHandler(socketserver.BaseRequestHandler):#相当于socket的recv方法 def handle(self): print(self.request) #new_socket print(self.client_address) #('127.0.0.1', 62685) print(self.server) #<socketserver.ThreadingTCPServer object at 0x033CFA50> server = socketserver.ThreadingTCPServer(('0.0.0.0',9999),MyHandler) print(server) #<socketserver.ThreadingTCPServer object at 0x033CFA50> server.serve_forever()
实现群聊
import threading from socketserver import ThreadingTCPServer,BaseRequestHandler import sys import logging FORMAT = '%(asctime)s %(threadName)d %(thread)d %(message)s' logging.basicConfig(level=logging.INFO,format=FORMAT) class ChatHandler(BaseRequestHandler): #一对一,相当于receive函数 clients = {} def setup(self): self.event = threading.Event() self.clients[self.client_address] = self.request def finish(self): self.clients.pop(self.client_address) self.event.set() def handle(self): print(self.request) #new_socket 用来recv while not self.event.is_set(): data = self.request.recv(1024) print(data,self.client_address) msg = '{}.ack'.format(data).encode() #如何实现一对多,多在哪里,如何获得 for c in self.clients.values(): c.send(msg) addr = ('0.0.0.0',9999) server = ThreadingTCPServer(addr,ChatHandler) #相当于tcp总的socket print(server) t = threading.Thread(target=server.serve_forever,name='chatserver') t.start() try: while True: cmd = input(">>>") if cmd.strip() == 'quit': break except Exception as e: print(e) except KeyboardInterrupt: pass finally: print('exit') sys.exit(0) 类的继承关系 BaseServer------>TCPServer---------->UDPServer | | UnixStreamServer UnixDatagramServer socketserver有四个同步类 TCPServer,UDPAServer,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是创建多线程 BaseRequestHandler类 它是用户连接的用户请求处理类的基类,定义为BaseRequestHandler(request,client_address,server) 服务端server实例接收用户请求后,最后会实例化这个类 它被实例化时,送入3个构建参数,request,client,address,server自身 以后就可以在BaseRequestHandler类的实例上使用一下属性 self.request是和客户端连接的socket对象 self.server是tcpserver本身 self.client_address是客户端的地址 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 官方文档例子 import socketserver class MyHandler(socketserver.BaseRequestHandler):#相当于socket的recv方法 def handle(self): print(self.request) #new_socket print(self.client_address) #('127.0.0.1', 62685) print(self.server) #<socketserver.ThreadingTCPServer object at 0x033CFA50> server = socketserver.ThreadingTCPServer(('0.0.0.0',9999),MyHandler) print(server) #<socketserver.ThreadingTCPServer object at 0x033CFA50> server.serve_forever() 利用socketserver实现群聊 import threading import socketserver from socketserver import ThreadingTCPServer,BaseRequestHandler import sys import logging FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s' logging.basicConfig(level=logging.INFO,format=FORMAT) class ChatHandler(BaseRequestHandler): #当服务端接收到新的请求后,baserequestHandler新类就会实例化 并一次执行setup ,try handle,finally finish clients = {} def setup(self): self.event = threading.Event() self.clients[self.client_address] = self.request def finish(self): self.clients.pop(self.client_address) self.event.set() def handle(self):#相当于socket的recv方法,每个不同的连接上请求过来后,生成连接的socket对象 #就是self.request,客户端地址self.client.address while not self.event.is_set(): data = self.request.recv(1024).decode() if data =='quit': break msg = "{} {}".format(self.client_address,data).encode() logging.info(msg) for c in self.clients.values(): self.request.send(msg) print('END') addr = ('0.0.0.0',9999) server = socketserver.ThreadingTCPServer(addr,ChatHandler) #baseserver源码serve_forever函数有try...finally关闭连接 server_thread = threading.Thread(target=server.serve_forever,name='ChatServer',daemon=True).start() try: while True: cmd = input("please set a exit code >>>") if cmd.strip() == 'quit': break except Exception as e: print(e) except KeyboardInterrupt: sys.exit(0
""" 通过打印可以看出,客户端主动断开连接,会导致revc方法立即返回一个空bytes,并没有同时抛出异常 当循环回到recv这一句话会抛出异常,所以通过判断data数据是否为空来判断客户端是否断开 """ def handle(self): while not self.event.is_set(): data = self.request.recv(1024) if not data or data =='quit': break """ 总结: 为每一个连接提供RequestHandlerClass类实例,依次调用setup,handle,finish方法,并且使用 try。。。finally结构保证finish方法一定被调用,这些方法一次执行完成,如果想维持这个连接和客户端通信 就需要在handler中循环使用 """