socketserver模块介绍
socketserver内部使用IO多路复用以及“多线程”和“多进程”,从而实现并发处理多个客户端请求的socket服务端,即:每个客户端请求连接到服务器时,socket服务端都会在服务器是创建一个“线程”或者“进程”专门负责处理当前客户端的所有请求
基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环
python中socketserver模块将网络服务抽象成两个主要的类
① Server类:用于解决链接问题,并且提供两个Mixln类,用于扩展server,实现多进程或者多线程
② RequesHandlert类:用于解决通信问题,也就是处理数据相关的操作
Server类:
RequestHandler类:
继承关系:
TCP:
############## 服务端 ##############
import socketserver
class MyServer(socketserver.BaseRequestHandler):
#通信
#1.BaseRequestHandler找到__init__
#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()
#自定义handle方法
def handle(self):
print('conn is',self.request) #conn
print('addr is',self.client_address) #addr
while True: #通信循环
try:
#收消息
data = self.request.recv(1024)
if not data:break
print('收到客户端信息是',data.decode('utf-8'))
#发消息
self.request.sendall(data.upper())
except Exception as e:
print(e)
break
if __name__ == '__main__':
s = socketserver.ThreadingTCPServer(('170.0.0.8',8030),MyServer) #多线程
#s = socketserver.ForkingTCPServer(('170.0.0.8',8030), MyServer) #多进程
#查找属性的顺序:ThreadingTCPServer->ThreadingMixIn->TCPServer->BaseServer
#链接
#1-> class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass -->> 先ThreadingMixIn,然后TCPServer
#2-> def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
#BaseServer.__init__(self, server_address, RequestHandlerClass)
#3->def __init__(self, server_address, RequestHandlerClass):
#self.server_address = server_address
#self.RequestHandlerClass = RequestHandlerClass
#因此ThreadingTCPServer实例化传进去的参数分别为server_address和RequestHandlerClass
print(s.server_address) #('170.0.0.8',8030)
print(s.RequestHandlerClass) #
print(MyServer) #
print(s.socket) #
s.serve_forever()
#1.BaseServer中找到def serve_forever(self, poll_interval=0.5):
#2.执行self._handle_request_noblock()
#3.执行request, client_address = self.get_request() 就是TCPServer中的self.socket.accept()
#然后执行self.process_request(request, client_address)
#4.ThreadingMixIn中找到process_request,开启多线程应对并发,进而执行process_request_thread
#5.最后self.finish_request(request, client_address)
#上面完成链接循环,BaseServer中找到finish_request,触发我们自己定义的类的实例化,下一步通信
############## 客户端 ##############
from socket import *
ip_port = ('170.0.0.8',8030)
buffer_size = 1024
tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)
while True:
msg = input('>>:').strip()
if not msg:continue #输入的命令为空时,继续进行下一次循环(输入)
if msg == 'quit':break #客户端断开连接
tcp_client.send(msg.encode('utf-8'))
data = tcp_client.recv(buffer_size)
print('收到服务端发送的消息',data.decode('utf-8'))
tcp_client.close()
UDP:
################## 服务端 ##################
import socketserver
class MyServer(socketserver.BaseRequestHandler):
def handle(self):
print(self.request)
print('收到客户端的消息是',self.request[0])
self.request[1].sendto(self.request[0].upper(),self.client_address)
if __name__ == '__main__':
s = socketserver.ThreadingUDPServer(('172.0.0.8',8050),MyServer) #多线程
#s = socketserver.ForkingTCPServer(('172.0.0.8',8050), MyServer) #多进程
#
#1.在TCPServer找到BaseServer.__init__,在BaseServer找到self.server_address = server_address
#self.RequestHandlerClass = RequestHandlerClass
print(s.server_address) #('172.0.0.8',8050)
print(s.RequestHandlerClass) #
print(MyServer) #
print(s.socket) #
s.serve_forever()
#1.self._handle_request_noblock()
#2.request, client_address = self.get_request()
#3.执行UDPServer中的get_request data, client_addr = self.socket.recvfrom(self.max_packet_size)
#得到return (data, self.socket), client_addr
#结论
#recvfrom的结果是((data,self.socket),client_addr) -->>self.get_request()得到的结果
#request对应的是(data,self.socket) , client_addr对应的是client_address
#有了socket对应的就是udp的套接字对象,可以调用sendto和recvfrom收发信息
################## 客户端 ##################
from socket import *
ip_port = ('172.0.0.8',8050)
buffer_size = 1024
udp_client = socket(AF_INET,SOCK_DGRAM)
while True:
msg = input('>>:').strip()
if not msg:continue
if msg == 'quit':break
udp_client.sendto(msg.encode('utf-8'),ip_port)
data,addr = udp_client.recvfrom(buffer_size)
print('收到服务端发送的消息',data.decode('utf-8'))
udp_client.close()
源码分析总结:
基于tcp的socketserver,我们自己定义的类中的:
① self.server即套接字对象:self.socket
② self.request即一个链接:conn
③ self.client_address即客户端地址:client_addr
基于udp的socketserver,我们自己定义的类中的:
① self.request是一个元组(第一个元素是客户端发来的数据:data,第二部分是服务端的udp套接字对象:self.socket)
② self.client_address即客户端地址:client_addr