黏包现象和解决黏包的方法
一, 缓冲区: 将程序和网络解耦
输入缓冲区
输出缓冲区
二, sunbprocess模块
import subprocess
sub_obj = subprocess.Popen(
'ipconfig', #系统指令
shell=True, #固定
stdout=subprocess.PIPE, #标准输出 PIPE 管道,保存着指令的执行结果
stderr=subprocess.PIPE )#标准错误输出
a = sub_obj.stdout.read() #执行"ipconfig"命令的结果,且a为字节类型
b = sub_obj.stderr.read() #错误输出,但"ipconfig"命令可执行,所以b为空
print('正确输出',a.decode('gbk')) #因为操作系统的编码为"gbk",所以a字节应该用"gbk"来解码
print('错误输出',b.decode('gbk')) #如果命令不可以被执行,b.decode('gbk')会打印 不是内部或外部命令...
三, 两种黏包现象:
1 连续的小包可能会被优化算法给组合到一起进行发送
2 第一次如果发送的数据大小2000B接收端一次性接受大小为1024,这就导致剩下的内容会被下一次recv接收到,导致结果错乱(看代码)
黏包的服务端
import socket
import subprocess
server = socket.socket()
ip_port = ("127.0.0.1",8001)
server.bind(ip_port)
server.listen()
conn,add = server.accept()
while 1 :
from_client = conn.recv(1024).decode("utf-8")
print(from_client)
sub_obj = subprocess.Popen(
from_client,
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_cmd_result = client.recv(1024)
print(server_cmd_result.decode('gbk'))
当客户端输入"ipconfig/all"之后,服务端发送给客户端的内容最多为1024字节,不能完全发过来.当客户端再输入一个"dir"命令后,之前没发完的消息会继续传给客户端,这就是黏包
解决黏包现象有两种方案
方案一:由于双方不知道对方发送数据的长度,导致接收的时候,可能接收不全,或者多接收另外一次发送的信息内容,所以在发送真实数据之前,要先发送数据的长度,接收端根据长度来接收后面的真实数据,但是双方有一个交互确认的过程
方案二: (重点)
Struct模块,
打包:struct.pack(‘i’,长度)
解包:struct.unpack(‘i’,字节) ,解包出来是元组,需要拿到元组的第0项索引的数据,就是之前的长度
解决黏包现象的服务端
import socket
import subprocess
import struct
server = socket.socket()
ip_port = ("127.0.0.1",8003)
server.bind(ip_port)
server.listen()
conn,add = server.accept()
while 1 :
# 收到客户端发来的消息,并进行解码
from_client = conn.recv(1024).decode("utf-8")
print(from_client)
# 把收到的消息,放在"subprocess"模块中执行"Popen"方法(执行这条消息的指令)
sub_obj = subprocess.Popen(
from_client,
shell = True,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
# 得到执行命令的结果,该结果为字节
std_msg = sub_obj.stdout.read()
#打印这个结果(字节)的长度
print("执行指令的长度:",len(std_msg))
# 把这个长度在"struct"模块中执行"pack"方法,"i"参数是把数字编译成4位字节(打包)
dabao = struct.pack("i",len(std_msg))
# 把执行指令的结果,前面加上这个结果的长度(4位字节),(把打包"dabao"嵌在真实结果的包头)发送到客户端
conn.send(dabao+std_msg)
解决黏包现象的客户端
import socket
import struct
client = socket.socket()
client.connect(('127.0.0.1',8003))
while 1:
cmd = input('请输入指令:')
# 把输入的指令进行编码,传到服务端端
client.send(cmd.encode('utf-8'))
# 收到服务端发过来的消息(4个字节),其实就是服务端打包的执行命令的真实内容的长度.
server_len_a = client.recv(4)
# 在"struct"模块中执行"unpack"方法,把收到的4个字节解包
# 取出该解包之后的元组中的索引为0的元素,该元素就是执行命令的(字节的)长度
# 重点:解包出来的是元组,且只有一个元素
server_len = struct.unpack('i',server_len_a)[0]
# 再次收到服务端发过来的消息,"recv"后面的参数为执行命令的(字节的)长度
server_msg = client.recv(server_len)
# 因为操作系统编码为"gbk",所以执行命令的(字节类型)解码为"gbk"
print(server_msg.decode('gbk'))
浙公网安备 33010602011771号