粘包
粘包现象
就是客户端发送到服务端的命令请求,服务端响应后发送结果回客户端,如果客户端接收的字节数小于结果的字节数,就会导致结果收不完,要在下一次的接收中才能再次把上一次没有接收完的数据收回来。
粘包底层原理分析
后面补充
解决粘包问题-简单版
服务端:
import socket
import subprocess
import struct
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.bind(("127.0.0.1", 9090))
phone.listen(5)
while True:
print("等待连接...")
conn, client_addr = phone.accept()
print("建立连接,客户端地址为:", client_addr)
while True:
try:
# 1. 接收命令
cmd = conn.recv(8196)
if not cmd: break
# 2. 执行命令,拿到结果
obj = subprocess.Popen(cmd.decode("GBK"), # 使用GBK解码
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout = obj.stdout.read() # 讲对象中的数据读出来
stderr = obj.stderr.read() # 讲对象中的数据读出来
# 3. 把命令的结果返回给客户端
# 3.1 制作固定长度的报头
total_size = len(stdout) + len(stderr)
header = struct.pack("i", total_size) # 生成一个固定长度为4的报头
# 3.2 把报头发送到客户端
conn.send(header) # header本身就是byte类型的,可以直接发送
# 3.3 发送真实的数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
phone.close()
客户端:
import socket
import struct
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 9090))
while True:
cmd = input(">>>: ").strip()
if not cmd: continue
phone.send(cmd.encode("GBK")) # 使用GBK编码,因为我在Windows上运行
# 1. 接收报头信息
header = phone.recv(4)
# 2. 解析报头拿到真实数据的长度
total_size = struct.unpack("i", header)[0]
# 3. 接收真实数据
recv_size = 0
recv_data = b''
while recv_size < total_size:
data = phone.recv(1024)
recv_data += data
recv_size += len(data)
print(recv_data.decode("GBK")) # 使用GBK解码
phone.close()
解决粘包问题-终极版
服务端:
import socket
import subprocess
import struct
import json
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.bind(("127.0.0.1", 9090))
phone.listen(5)
while True:
print("等待连接...")
conn, client_addr = phone.accept()
print("建立连接,客户端地址为:", client_addr)
while True:
try:
# 1. 接收命令
cmd = conn.recv(8196)
if not cmd: break
# 2. 执行命令,拿到结果
obj = subprocess.Popen(cmd.decode("GBK"), # 使用GBK解码
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout = obj.stdout.read() # 讲对象中的数据读出来
stderr = obj.stderr.read() # 讲对象中的数据读出来
# 3. 把命令的结果返回给客户端
# 3.1 制作报头
header_dict = {
"filename": "abc.txt",
"md5": "xxxxxxx",
"total_size": len(stdout) + len(stderr)
}
header_json = json.dumps(header_dict) # 将报头字典序列化为字符串类型
header_bytes = header_json.encode("GBK") # 将序列化后字符串类型的报头编码为bytes类型
# 3.2 发送报头长度
conn.send(struct.pack('i', len(header_bytes)))
# 3.3 发送报头
conn.send(header_bytes)
# 3.4 发送真实的数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break
conn.close()
phone.close()
客户端:
import socket
import struct
import json
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(("127.0.0.1", 9090))
while True:
cmd = input(">>>: ").strip()
if not cmd: continue
phone.send(cmd.encode("GBK")) # 使用GBK编码,因为我在Windows上运行
# 1. 接收报头长度
obj = phone.recv(4)
head_size = struct.unpack("i", obj)[0]
# 2. 接收报头
header_bytes = phone.recv(head_size)
# 3. 将报头解码
header_json = header_bytes.decode("GBK")
# 4. 将报头反序列化为json
header_dict = json.loads(header_json)
# 5. 获取到json里的真实数据的大小
total_size = header_dict["total_size"]
# 4. 接收真实数据
recv_size = 0
recv_data = b''
while recv_size < total_size:
data = phone.recv(1024)
recv_data += data
recv_size += len(data)
print(recv_data.decode("GBK")) # 使用GBK解码
phone.close()
共有 0 条评论