day8-socketserver使用
概述
我们之前所学的socket都是一个人上传和下载文件,它不支持多用户,多并发同时处理,所以就出现了SocketServer,它产生的原因就是能够实现并发处理。
SocketServer模块简化了编写SocketServer的任务
SocketServer的四种类
TCPServer
说明:使用Internet TCP协议,它在客户端和服务器之间提供连续的数据流。如果bind_and_activate为true,则构造函数自动尝试调用server_bind()和server_activate().其他参数传递给BaseServer基类。
class socketserver.TCPServer(server_address, RequestHandlerClass, bind_and_activate=True)
UDPServer
说明:使用UDP协议,这些数据包是离散的数据包,所以可能不按顺序到达或在传输途中丢失。参数与TCPServer相同
class socketserver.UDPServer(server_address, RequestHandlerClass, bind_and_activate=True)
UnixStreamServer和UnixDatagramServer
说明:这些较少使用的类与TCP和UDP类相似,但使用Unix域socket;他们在非Unix平台上不可用。参数与TCPServer相同
class socketserver.UnixStreamServer(server_address, RequestHandlerClass, bind_and_activate=True)
class socketserver.UnixDatagramServer(server_address, RequestHandlerClass,bind_and_activate=True)
小结
这四个类同步处理请求;每个请求必须在下一个请求可以开始之前完成。如果每个请求需要很长时间才能完成,这是不合适的,因为它需要大量的计算,或者因为它返回了很多客户端处理缓慢的数据。解决方案是创建一个单独的进程或线程来处理每个请求; ForkingMixIn和ThreadingMixIn混合类可以用来支持异步行为。
继承图中有五个类,其中四个代表四种类型的同步服务器
+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
注意:UnixDatagramServer派生自UDPServer,而不是来自UnixStreamServer - IP和Unix流服务器之间的唯一区别是地址族,这在两个Unix服务器类中都是重复的。
创建SocketServer的步骤
- 首先,您必须通过继承BaseRequestHandler类并重写handle()方法来创建请求处理类;这个方法将处理传入的请求。
- 其次,您必须实例化一个Server类(4种其中1种),并将参数Server的IP地址和上面创建的请求处理类传递给Server类。
- 然后调用服务器对象的handle_request()或server_forever()方法来处理一个或多个请求。
- 最后,调用server_close()关闭socket。
简单SocketServer代码实现
服务器端
思路:创建Handle处理类 => 重写handle()方法(处理传入请求) => 实例化Server类 => 调用server_forever()方法处理多个客户端请求
import socketserver class MyTCPHandler(socketserver.BaseRequestHandler): """ 服务器的请求处理类 每次连接到服务器都会被实例化一次,并且必须重写handle()方法来实现与之通信的客户端 """ def handle(self): while True: try: # self.request 是TCP socket连接到客户端 self.data = self.request.recv(1024).strip() print("{} wrote:".format(self.client_address[0])) print(self.data) if not self.data: #如果没有收到数据,说明客户端已断开 print("客户端{}已经断开!".format(self.client_address[0])) break # 发回同样的数据,只是变成了大写 self.request.send(self.data.upper()) except ConnectionResetError as e: print("error",e) break if __name__ == "__main__": HOST, PORT = "localhost", 9995 # 创建一个Server对象(实例化)并绑定IP地址和端口 server = socketserver.TCPServer((HOST,PORT), MyTCPHandler) # 激活Server后,它将一直运行直到使用Ctrl-C去中断它 server.serve_forever() #运行输出 127.0.0.1 wrote: b'ls' 127.0.0.1 wrote: b'ls' 127.0.0.1 wrote: b'' 客户端127.0.0.1已经断开!
解析:MyTCPHandler()类是自己写的一个请求处理类,这个请求处理类继承socketserver,而且要重写handle()方法,这个handle()方法默认为空,客户端到服务器的每次请求都会被实例化,接收数据不再是之前的conn.recv(),而必须是self.request.recv(),同样发送数据给客户端也是self.request。
注意:服务器端跟客户端所有的交互都是在handle()里完成的,每一个从客户端发来的请求,它的请求都会被分配到def handle()进行处理,并且处理过程就是handle()规定的。
客户端
import socket client = socket.socket() client.connect(("localhost",9995)) while True: cmd_input = input(">>:") if len(cmd_input) == 0: continue client.send(cmd_input.encode()) data = client.recv(1024) print(data.decode()) #运行输出 >>:ls LS >>:dir DIR