什么是粘包?
有缓存区,缓存区里放响应的字节,满了后,将剩下的存起来
等下次再接受信息的时候,先将上次剩下的输出
再次接受信息的时候,将上次没有输出的信息输出
在TCP协议中,发送方发送的若干包数据到接收方,接收时粘成一包
从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。
为什么设置缓冲区?
断网后数据会丢失
下载数据断网后再联网做个缓冲控制速度均衡
1、暂时存储一些数据
2、网络有波动,保证数据的收发稳定和均速
确定:造成了粘包现象之一
什么情况下出现粘包
1、send:数据过大,大过对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余数据
2、send:连续短暂的send多次(传送数据量较小),统一存到了recv缓存区;再统一发送出去
多少算小
展示一些收发的内容
只要send就要转成bytes类型
如何解决粘包现象
服务端发一次数据:10000字节
客户端接受数据时,循环接收,每次最多接收1024个字节,直到将10000所有的字节全部接收完毕,
将接收的数据拼接在一起,最后解码
1、遇到的问题:recv的次数无法确定
发送总具体数据之前,先给一个总数据的长度,然后再发总数据
客户端先接收一个长度,再循环recv控制循环的条件就是只要接收的数据小于总长度,就一直循环
2、遇到的问题:
总数据的长度转成字节
要解决将不固定长度的int类型转成固定的bytes字节,且还能翻转回来
要将total_size int类型转成bytes类型才能发送
服务端:
conn.send(total_size)
conn.send(result)
total_size int 类型
客户端:
total_size_bytes = phone.recv(4)
total_size
data = b''
while len(data) < total_size:
data = data + phone.recv(1024)
将total_size int 类型转化成bytes类型才可以发送
将不固定长度的int类型转化成固定长度的bytes并且可以翻转回来
387 --> str(387)'387' -->bytes b'387' 长度 3bytes
4185 --> str(4185)'4185' --> bytes b'387' 长度 4bytes
low 版解决方案
server端
# 1. 粘包第一种: send的数据过大,大于对方recv的上限时,对方第二次recv时,会接收上一次没有recv完的剩余的数据。
import socket
import subprocess
import struct
phone = socket.socket()
phone.bind(('127.0.0.1',8848))
phone.listen(2)
# listen: 2 允许有两个客户端加到半链接池,超过两个则会报错
while 1:
conn,addr = phone.accept() # 等待客户端链接我,阻塞状态中
# print(f'链接来了: {conn,addr}')
while 1:
try:
from_client_data = conn.recv(1024) # 接收命令
if from_client_data.upper() == b'Q':
print('客户端正常退出聊天了')
break
obj = subprocess.Popen(from_client_data.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
result = obj.stdout.read() + obj.stderr.read()
total_size = len(result)
print(f'总字节数:{total_size}')
# 1. 制作固定长度的报头
head_bytes = struct.pack('i',total_size)
# 2. 发送固定长度的报头
conn.send(head_bytes)
# 3. 发送总数据
conn.send(result)
except ConnectionResetError:
print('客户端链接中断了')
break
conn.close()
phone.close()
# import struct
# # 将一个数字转化成等长度的bytes类型。
# ret = struct.pack('i', 180000000)
# # print(ret, type(ret), len(ret))
#
# # 通过unpack反解回来
# ret1 = struct.unpack('i',ret)[0]
# # print(ret1)
# print(ret1, type(ret1))
# 总数据:总数据长度
# s1 = 'fjdslf太白金星jsfk疯狂夺金分离式的疯狂的数量方式登记拉开lagfdkjglkhjklh'
# b1 = s1.encode('utf-8')
# print(b1)
# print(len(b1))
client
import socket
import struct
phone = socket.socket()
phone.connect(('127.0.0.1',8848))
while 1:
to_server_data = input('>>>输入q或者Q退出').strip().encode('utf-8')
if not to_server_data:
# 服务端如果接受到了空的内容,服务端就会一直阻塞中,所以无论哪一端发送内容时,都不能为空发送
print('发送内容不能为空')
continue
phone.send(to_server_data)
if to_server_data.upper() == b'Q':
break
# 1. 接收报头
head_bytes = phone.recv(4)
# 2. 反解报头
total_size = struct.unpack('i',head_bytes)[0]
total_data = b''
while len(total_data) < total_size:
total_data += phone.recv(1024)
print(len(total_data))
print(total_data.decode('gbk'))
phone.close()