python--subprocess,粘包现象与解决办法,缓冲区
一. subprocess 的简单用法
import subprocess sub_obj = subprocess.Popen( 'dir', #系统指令 shell=True, #固定方法 stdout=subprocess.PIPE, #标准输出 PIPE 管道,保存着指令的执行结果 stderr=subprocess.PIPE #标准错误输出 ) # dir 当前操作系统(Windows)的命令,会执行stdout print('正确输出',sub_obj.stdout.read().decode('gbk')) # 如果是 'ls' 是Linux里的命令 会执行stderr ,因为系统的编码是gbk print('错误输出',sub_obj.stderr.read().decode('gbk'))
二 .两种粘包现象
1 连续的小包可能会被优化算法给组合到一起进行发送
# 客户端 import socket BUFSIZE=1024 ip_port=('127.0.0.1',8080) s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) # res=s.connect_ex(ip_port) res=s.connect(ip_port) # 这边分两段发送 s.send('hi'.encode('utf-8')) s.send('meinv'.encode('utf-8')) # 服务端 from socket import * ip_port=('127.0.0.1',8080) tcp_socket_server=socket(AF_INET,SOCK_STREAM) tcp_socket_server.bind(ip_port) tcp_socket_server.listen(5) conn,addr=tcp_socket_server.accept() #服务端连接接收两个信息 data1 = conn.recv(10) data2 = conn.recv(10) # 如果网络良好的话 收到的应该是 一条信息himeinv print('----->',data1.decode('utf-8')) print('----->',data2.decode('utf-8')) conn.close()
2 第一次如果发送的数据大小2000B接收端一次性接受大小为1024,这就导致剩下的内容会被下一次recv接收到,导致结果错乱
# 客户端 import socket client = socket.socket() client.connect(('127.0.0.1',8001)) while 1: cmd = input('请输入指令:') client.send(cmd.encode('utf-8')) # 这里写1025是因为粘包的原因需要写1025才能正好接收到完整的字 server_cmd_result = client.recv(1025) print(server_cmd_result.decode('gbk')) # 服务端 import socket import subprocess server = socket.socket() ip_port = ('127.0.0.1',8001) server.bind(ip_port) server.listen() conn,addr = server.accept() while 1: from_client_cmd = conn.recv(1024) print(from_client_cmd.decode('utf-8')) sub_obj = subprocess.Popen( from_client_cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) std_msg = sub_obj.stdout.read() print('指令的执行结果长度>>>>',len(std_msg)) conn.send(std_msg)
三 . 解决粘包现象的方法
方案一:由于双方不知道对方发送数据的长度,导致接收的时候,可能接收不全,或者多接收另外一次发送的信息内容,所以在发送真实数据之前,
要先发送数据的长度,接收端根据长度来接收后面的真实数据,但是双方有一个交互确认的过程.
# 客户端 import socket client = socket.socket() client.connect(('127.0.0.1',8001)) while 1: cmd = input('请输入指令:') client.send(cmd.encode('utf-8')) server_res_len = client.recv(1024).decode('utf-8') print('来自服务端的消息长度',server_res_len) # 告知服务端已经接收到了长度 client.send(b'ok') # 接收所有发过来的所有字节的长度 server_cmd_result = client.recv(int(server_res_len)) print(server_cmd_result.decode('gbk')) # 服务端 import socket import subprocess server = socket.socket() ip_port = ('127.0.0.1',8001) server.bind(ip_port) server.listen() conn,addr = server.accept() while 1: from_client_cmd = conn.recv(1024) print(from_client_cmd.decode('utf-8')) #接收到客户端发送来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令 sub_obj = subprocess.Popen( from_client_cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, #正确结果的存放位置 stderr=subprocess.PIPE #错误结果的存放位置 ) #从管道里面拿出结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果 std_msg = sub_obj.stdout.read() #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据 std_msg_len = len(std_msg) #首先将数据长度的数据类型转换为bytes类型 std_bytes_len = str(len(std_msg)).encode('utf-8') print('指令的执行结果长度>>>>',len(std_msg)) conn.send(std_bytes_len) # 确认一下客户端是否收到 status = conn.recv(1024) if status.decode('utf-8') == 'ok': conn.send(std_msg) else: pass
方案二:
struct模块,
打包:struct.pack(‘i’,长度)
解包:struct.unpack(‘i’,字节)
# struct 的简单用法 import struct num = 100 #打包,将int类型的数据打包成4个长度的bytes类型的数据 byt = struct.pack('i',num) print(byt) # b'd\x00\x00\x00' #解包,将bytes类型的数据,转换为对应的那个int类型的数据 # int_num = struct.unpack('i',byt) # print(int_num) # (100,) int_num = struct.unpack('i',byt)[0] print(int_num) # 100
# 客户端 import socket import struct client = socket.socket() client.connect(('127.0.0.1',8001)) while 1: cmd = input('请输入指令:') #发送指令 client.send(cmd.encode('utf-8')) #接收数据长度,首先接收4个字节长度的数据,因为这个4个字节是长度 server_res_len = client.recv(4) msg_len = struct.unpack('i',server_res_len)[0] print('来自服务端的消息长度',msg_len) #通过解包出来的长度,来接收后面的真实数据 server_cmd_result = client.recv(msg_len) print(server_cmd_result.decode('gbk')) # 服务端 import socket import subprocess import struct server = socket.socket() ip_port = ('127.0.0.1',8001) server.bind(ip_port) server.listen() conn,addr = server.accept() while 1: from_client_cmd = conn.recv(1024) print(from_client_cmd.decode('utf-8')) #接收到客户端发送来的系统指令,我服务端通过subprocess模块到服务端自己的系统里面执行这条指令 sub_obj = subprocess.Popen( from_client_cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, #正确结果的存放位置 stderr=subprocess.PIPE #错误结果的存放位置 ) #从管道里面拿出结果,通过subprocess.Popen的实例化对象.stdout.read()方法来获取管道中的结果 std_msg = sub_obj.stdout.read() #为了解决黏包现象,我们统计了一下消息的长度,先将消息的长度发送给客户端,客户端通过这个长度来接收后面我们要发送的真实数据 std_msg_len = len(std_msg) print('指令的执行结果长度>>>>',len(std_msg)) msg_lenint_struct = struct.pack('i',std_msg_len) conn.send(msg_lenint_struct+std_msg)
四 . 缓冲区