转载自:网络编程 --- subprocess模块,struct模块,粘包,UDP协议,socket_server模块 - 余人。 - 博客园
目录
作用:
1.可以帮你通过代码执行操作系统的终端命令
2.并返回终端执行命令后的结果
import subprocess cmd = input('cmd>>') obj = subprocess.Popen( cmd, shell=True, stderr=subprocess.PIPE, # 返回错误结果参数 stdout=subprocess.PIPE # 返回正确结果参数 ) result = obj.stdout.read() + obj.stderr.read() print(result.decode('gbk')) # 直接调用cmd进行工作,记得使用gbk解码
是一个可以将很长的数据的长度,压缩成固定的长度的一个标记(数据报头)
import struct #打包压缩 # i模式,会将数据长度压缩成4个bytes import struct str1 = 'dfhkashklfhasfhhlhffihiurue' herders = struct.pack('i',len(str1)) print(herders) # b'\x1b\x00\x00\x00' print(len(herders)) # 4,压缩成的报头长度为4 date_len = struct.unpack('i',herders) print(date_len[0]) #打印结果为元组行式,可以按照索引取值来将值取出
粘包问题
服务端第一次发送的数据,客户端无法精确一次性接收完毕。下一次发送的数据与上一次数据粘在一起了。
1.无法预测对方需要接收到数据大小长度。
2.多次连续发送数据量小,并且时间间隔短的数据一次性打包发送
TCP协议特性:
TCP是一个流式协议,会将多次连续发送的数据量小,并且时间间隔短的数据一次性打包发送
解决粘包问题
--struct模块
必须先定义报头,发送报头,再发送真实数据
- 既想发送文件,又想发送文件的描述信息 # 客户端发送字典给服务端 send_dic = { file_name: 文件名 file_size: 文件的真实长度 1000000 } # 通过json模块序列化成bytes数据 json_data = json.dumps(send_dic) bytes_data = json_data.encode('utf-8') # bytes # 先获取字典的报头 headers = struct.pack('i', len(bytes_data)) # 服务端接收到字典,并接收文件的真实数据
解决粘包问题的方法
服务端: import socket import json import struct server = socket.socket() server.bind( ('127.0.0.1', 9527) ) server.listen(5) while True: conn, addr = server.accept() while True: try: # 先接收报头 headers = conn.recv(4) # 解包获取真实数据长度 data_len = struct.unpack('i', headers)[0] # 获取字典数据真实长度 bytes_data = conn.recv(data_len) back_dic = json.loads(bytes_data.decode('utf-8')) print(back_dic) except Exception as e: print(e) break conn.close() 客户端: import socket import struct import json import time client = socket.socket() client.connect( ('127.0.0.1', 9527) ) while True: send_dic = { 'file_name': 'jason真实写真集.avi', 'file_size': 10000000 # 10G } # json序列化,并转码成bytes类型数据 json_data = json.dumps(send_dic) bytes_data = json_data.encode('utf-8') # 先做报头 headers = struct.pack('i', len(bytes_data)) client.send(headers) client.send(bytes_data) time.sleep(10)
上传大文件的方法
服务端: import socket import json import struct server = socket.socket() server.bind( ('127.0.0.1', 9527) ) server.listen(5) while True: conn, addr = server.accept() try: # 先接收字典报头 headers = conn.recv(4) # 解包获取字典真实数据长度 data_len = struct.unpack('i', headers)[0] # 获取字典真实数据 bytes_data = conn.recv(data_len) # 反序列得到字典 back_dic = json.loads(bytes_data.decode('utf-8')) print(back_dic) # 拿到字典的文件名,文件大小 file_name = back_dic.get('file_name') file_size = back_dic.get('file_size') init_data = 0 # 1.以文件名打开文件,准备写入 with open(file_name, 'wb') as f: # 一点一点接收文件,并写入 while init_data < file_size: data = conn.recv(1024) # 2.开始写入视频文件 f.write(data) init_data += len(data) print(f'{file_name}接收完毕!') except Exception as e: print(e) break conn.close() 客户端 import socket import struct import json client = socket.socket() client.connect( ('127.0.0.1', 9527) ) # 1.打开一个视频文件,获取视频数据大小 with open(r'D:\jason真实写真集.mp4', 'rb') as f: movie_bytes = f.read() # 关闭文件 # 2.为视频文件组织一个字典,字典内有视频的名称,视频大小 send_dic = { 'file_name': 'jason真实写真集.mp4', 'file_size': len(movie_bytes) # 10G } # 3.先打包字典,发送headers报头,再发送真实字典数据 json_data = json.dumps(send_dic) bytes_data = json_data.encode('utf-8') headers = struct.pack('i', len(bytes_data)) # 发送报头 client.send(headers) # 发送真实字典数据 client.send(bytes_data) # 4.接着发送真实视频文件数据 init_data = 0 num = 1 with open(r'D:\jason真实写真集.mp4', 'rb') as f: while init_data < len(movie_bytes): # 最后一次获取,有多少拿多少 send_data = f.read(1024) print(send_data, num) num += 1 # 每次发送1024数据 client.send(send_data) # 为初始发送数据 + 已发送数据的长度 init_data += len(send_data)
定义:UDP是一种传输协议
特性
1.不需要建立双向管道
2.不会粘包
3.客户端给服务端发送数据,不需要等待服务端返回结果成功
4.数据容易丢失,数据不安全
使用方法
服务端 import socket # SOCK_DGRAM: 代表UDP server = socket.socket(type=socket.SOCK_DGRAM) # 服务端需要绑定ip+port server.bind( ('127.0.0.1', 9527) ) # TCP # conn, addr = server.accept() # conn.recv() # UDP msg, addr = server.recvfrom(1024) msg1, addr1 = server.recvfrom(1024) msg2, addr2 = server.recvfrom(1024) print(msg, msg1, msg2) 客户端 import socket client = socket.socket(type=socket.SOCK_DGRAM) server_ip_port = ('127.0.0.1', 9527) client.sendto(b'hello', server_ip_port) client.sendto(b'hello', server_ip_port) client.sendto(b'hello', server_ip_port) client.sendto(b'hello', server_ip_port) client.sendto(b'hello', server_ip_port)
python内置模块,可以简化socket套接字服务端的代码
简化TCP与UDP服务端代码
必须要创建一个类
使用方法
# 定义类 # TCP: 必须继承BaseRequestHandler类 class MyTcpServer(socketserver.BaseRequestHandler): # 必须重写父类的handle def handle(self): while True: try: # 1.接收消息 data = self.request.recv(1024).decode('utf-8') # conn.recv(1024) send_msg = data.upper() self.request.send(send_msg.encode('utf-8')) except Exception as e: print(e) break if __name__ == '__main__': server = socketserver.ThreadingTCPServer( ('127.0.0.1', 8888), MyTcpServer ) # 永久执行服务 server.serve_forever()