socketserver
是标准库中的一个高级模块,socketserver
模块是python提供的内置的用于快捷开发服务端程序的一个服务器框架,经过封装大量实现的方式减小开发人员工做量的同时能快捷开发出具备较高质量的服务端程序; 该模块中类的继承关系以下:python
+------------+ | BaseServer | +------------+ | v +-----------+ +------------------+ | TCPServer |------->| UnixStreamServer | +-----------+ +------------------+ | v +-----------+ +--------------------+ | UDPServer |------->| UnixDatagramServer | +-----------+ +--------------------+
socketserver模块主要包含的非Unix服务器类有:TCPserver
、UCPserver
、ThreadingTCPServer(ThreadingMixIn, TCPServer)
、ThreadingUDPServer(ThreadingMixIn, UDPServer)
、ForkingTCPServer(ForkingMixIn, TCPServer)
、ForkingUDPServer(ForkingMixIn, UDPServer)
,其中的ThreadingMixIn、ForkingMixIn分别用来实现线程级别、进程级别的异步浏览器
TCPServer
,基本的网络同步TCP服务器;服务器
UDPServer
,基本的网络同步UDP服务器;网络
UnixStreamServer
,使用UNIX域套接字实现面向数据流协议的服务器,继承自TCPServer;多线程
UnixDatagramServer
,使用UNIX域套接字实现数据报协议的服务器,继承自UDPServer;框架
其中经常使用的是TCPServer
和UDPServer
,且这四个类型都是同步地处理请求,也就是说一个请求没有完成以前是不会处理下一个请求的,可是这种模式不适合生产环境。 因此这个模块还提供了两种支持异步处理的类,ForkingMixIn
和ThreadingMixIn
,继承自这两个类的服务端在处理新的客户端链接时不会阻塞,而是建立新的进程或线程来专门处理客户端的请求:异步
ForkingMixIn
,将UNIX进程分支添加到服务器的混合方法,使用该方法可让服务器服务多个客户,(为每个客户端请求派生一个新的进程去专门处理);socket
ThreadingMixIn
,修改服务器的混合类,可使用多线程服务多个客户端,(为每个客户端请求派生一个新的线程去专门处理);tcp
咱们先编写服务器端的代码,首先导入了socketserver模块,而后自定义一个Handler类,这个类是继承了socketserver模块中的BaseRequestHandler。 咱们能够按住Ctrl键而后鼠标点击这个类,看一下这个类的方法有哪些,除了一个__init__()
方法,是否是还有三个没有实现的setup()、handle()、finish()方法呀,那这里就在Handler类中覆盖BaseRequestHandler类的handle()方法,打印一些信息。 而后选择一个合适的Server类,初始化且绑定地址和新建的Handler处理类,最后调用server实例的serve_forever()
方法,启动server。函数
import socketserver import threading from pprint import pprint class Handler(socketserver.BaseRequestHandler): def handle(self): print('当前的server类型: {}'.format(self.server)) print('当前的socket链接对象: {}'.format(self.request)) print('当前的客户端地址: {}'.format(self.client_address)) print('线程列表: {}'.format(threading.enumerate())) print('当前的线程:{}'.format(threading.current_thread())) server = socketserver.TCPServer(('127.0.0.1', 9000), Handler) server.serve_forever()
咱们运行一下这个代码,发现控制台并无输出,那咱们能够到命令窗口查看一下TCP是否链接,命令行为:netstat -anp tcp | grep 9000
,而后显示的是正在监听并无链接,咱们能够直接使用浏览器请求9000端口,由于HTTP是在TCP之上实现的一种协议,而后在浏览器中访问127.0.0.1: 9000
,控制台中打印出不少的信息:
当前的server类型: <socketserver.TCPServer object at 0x000000000277DD68> 当前的socket链接对象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63944)> 当前的客户端地址: ('127.0.0.1', 63944) 线程列表: [<_MainThread(MainThread, started 7612)>] 当前的线程:<_MainThread(MainThread, started 7612)> 当前的server类型: <socketserver.TCPServer object at 0x000000000277DD68> 当前的socket链接对象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63945)> 当前的客户端地址: ('127.0.0.1', 63945) 线程列表: [<_MainThread(MainThread, started 7612)>] 当前的线程:<_MainThread(MainThread, started 7612)> 当前的server类型: <socketserver.TCPServer object at 0x000000000277DD68> 当前的socket链接对象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63948)> 当前的客户端地址: ('127.0.0.1', 63948) 线程列表: [<_MainThread(MainThread, started 7612)>] 当前的线程:<_MainThread(MainThread, started 7612)> 当前的server类型: <socketserver.TCPServer object at 0x000000000277DD68> 当前的socket链接对象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63949)> 当前的客户端地址: ('127.0.0.1', 63949) 线程列表: [<_MainThread(MainThread, started 7612)>] 当前的线程:<_MainThread(MainThread, started 7612)> 当前的server类型: <socketserver.TCPServer object at 0x000000000277DD68> 当前的socket链接对象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63950)> 当前的客户端地址: ('127.0.0.1', 63950) 线程列表: [<_MainThread(MainThread, started 7612)>] 当前的线程:<_MainThread(MainThread, started 7612)> 当前的server类型: <socketserver.TCPServer object at 0x000000000277DD68> 当前的socket链接对象: <socket.socket fd=224, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 63951)> 当前的客户端地址: ('127.0.0.1', 63951) 线程列表: [<_MainThread(MainThread, started 7612)>] 当前的线程:<_MainThread(MainThread, started 7612)>
那咱们总结一下,socketserver模块建立服务器步骤:
setup()
:该方法在handle()以前调用,默认什么都不作,若是但愿服务器实现更多链接设置,则无需调用该方法;
handle()
:调用该方法执行实际的请求操做,调用函数能够不带任何参数,默认什么都不作;
finish()
: 环境清理,在handle()以后执行清除操做,默认什么都不作,若是setup()和handle()方法都不生成异常,则无需调用该方法;
############################## 服务器端 ############################## import socketserver import threading class Handler(socketserver.BaseRequestHandler): clients = {} def setup(self): super().setup() self.event = threading.Event() self.clients[self.client_address] = self.request def handle(self): super().setup() while not self.event.is_set(): data = self.request.recv(1024).decode() if data == 'quit': break msg = "{} 说 {}".format(self.client_address, data).encode() print(self.clients) if self.client_address in self.clients.keys(): self.clients[self.client_address].send(msg) # if request self.clients.values(): # request.send(msg) print('server end') def finish(self): super().finish() self.clients.pop(self.client_address) self.event.set() if __name__ == '__main__': server = socketserver.ThreadingTCPServer(('127.0.0.1', 9000), Handler) threading.Thread(target=server.serve_forever, daemon=True).start() while True: cmd = input('请输入您想说的话: ').strip() if cmd == 'quit': print(cmd) server.shutdown() server.server_close() break else: print('您能够输入quit来中止服务器! ')
############################## 客户端 ############################## import socket # 建立TCP链接 socket_instance = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socket_instance.connect(('127.0.0.1', 9000)) while True: cmd = input("请输入您想说的话:") socket_instance.send(cmd.encode()) data = socket_instance.recv(1024).decode() print(data)