一、文件上传/下载python
学习了socket套接字,咱们如今能够写一个文件上传/下载的程序,以下示例:算法
分析上边代码,咱们发现,client发送上传文件相关信息的字典序列化以后,server又给client发送了一个状态码,而后client才开始上传文件数据,思考一下若是不发送状态码,直接发送文件数据且避免黏包现象发生该怎么写?没错!能够用struct模块解决,以下示例:并发
struct(四chua科特)打包如何用呢,简单写一下:socket
import struct num = 156 #将int类型的数据打包成4个字节的数据 num_stru = struct.pack('i',num) #pack(爬可) print(len(num_stru)) #他的长度四个字节 print(num_stru) print('11111111111111111111111111111111') #在经过int类型解包,将前面打包的数据解包成打包以前的int数据 num2 = struct.unpack('i',num_stru) #解包出来是个元组 unpack(嗯牌可) print(num2)#(156,) print(num2[0])
服务端代码:tcp
客户端代码:学习
分析上面的代码,看看是如何避免黏包现象的?client先给server发送字典的长度的pack包,再发送字典,再发送文件数据,server先接收4字节的pack包,进行unpack后获得字典长度,再根据字典长度接收字典,最后再循环接收文件数据。而且该示例还进行了MD5算法来进行文件一致性校验。线程
2、socketserver(并发)(骚k特点我)3d
经过这两天学习socket套接字,咱们发如今写服务端和客户端的时候,在创建链接以前老是要写一些固定的重复代码来,好比socket.socket()(建立套接字对象)、bind()、acecept()等等,学习了socketserver(并发)以后,就能够少写一些代码,而且实现并发,以下示例:code
服务端:server
import socketserver #1 定义一个类 class MyServer(socketserver.BaseRequestHandler): #2 类里面继承socketserver.BaseRequestHandler #BaseRequestHandler贝斯蕊快四特汉得了 # 3 类里面定义一个handle方法,handle名称不能变 def handle(self): #handle(汉得了) while 1: # self.request #conn连接通道 request(蕊快死特) from_client_data = self.request.recv(1024).decode('utf-8') print(from_client_data) server_input = input('明巍sb说>>>') self.request.send(server_input.encode('utf-8')) # self.request.close() if __name__ == '__main__': #服务端的IP地址和端口 ip_port = ('127.0.0.1',8001) socketserver.TCPServer.allow_reuse_address = True #容许地址重用 #绑定IP地址和端口,而且启动我定义的上面这个类 server = socketserver.ThreadingTCPServer(ip_port,MyServer) #永久的给我执行下去 server.serve_forever()
客户端:
import socket tcp_client = socket.socket() server_ip_port = ('127.0.0.1',8001) tcp_client.connect(server_ip_port) while 1: client_msg = input('大阳哥>>>') tcp_client.send(client_msg.encode('utf-8')) from_server_msg = tcp_client.recv(1024).decode('utf-8') print(from_server_msg)
3、解读python中socketserver源码
结合下图中类的继承关系和类中的方法,分析socketserver代码的执行过程:
a、初始化相关过程:server = socketserver.ThreadingTCPServer(('127.0.0.1',8800),Myserver)
(1)TCPServer类中的__init__方法:
TCPServer类中主动执行BaseServer类中的__init__ 方法(把本身建立的Myserver类传参);
建立socket.socket()对象;
server_bind() -- 在TCPServer类中执行了socket.bind(self.server_address)
server_activate() -- 在TCPServer类中执行了socket.listen(5)
(2)BaseServer类中的__init__ 方法:
将参数server_address(ip地址和端口号)赋值给了self.server_address;
将形参RequestHandlerClass(实参是咱们本身建立的Myserver类)赋值给了self.RequestHandlerClass;
b、执行serve_forever的相关代码:
(1)执行BaseServer类中的serve_forever()方法:
注意看if ready后边的那句self._handle_request_noblock(),其余先忽略;
(2)执行BaseServer中的_handle_request_noblock(self)方法:
看第一个try中request,client_address = self.get_request(),
TCPServer中有get_request()方法,方法中是return self.socket.accept(),即等待链接;
再看第二个try中的self.process_request(request,client_address)
(3)链接成功以后拿到了request(即conn)和client_address(即addr)再去执行BaseServer类中的.process_request方法;
建立线程,执行方法process_request_thread()
(4)执行ThreadingMixIn 类中的process_request_thread(self, request, client_address)方法:
看try中self.finish_request(request,client_address)
(5)执行BaseServer中的finish_request(request,client_address)方法:
此时还记得RequestHandlerClass这个参数吗?正是咱们执行BaseServer中__init__方法时传过来的本身建立的类Myserver,类() 即实例化一个Myserver对象,而且传了两个参数(conn,addr),可是咱们本身写的Myserver类中没有__init__方法,因此执行Myserver父类BaseRequestHandler类中的__init__方法,而且带了每一个链接的线程的conn和addr:
(6)执行BaseRequestHandler中的__init__方法:
此时self是Myserver类的对象,因此优先去执行Myserver类中handle方法。
附录:如下是各种的继承关系以及类中成员方法: