黏包
黏包
tcp协议在发送数据时,会出现黏包现象.
- 数据粘包是因为在客户端/服务器的发送端和接收端都会有一个数据缓冲区, 缓冲区用来临时保存数据,默认空间都设置较大。在收发数据频繁时,由于tcp传输消息的无边界特点,不清楚应该截取多少长度,导致客户端/服务器端,都有可能把多条数据当成是一条数据进行截取,造成黏包
- 发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个数据包。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后在发送,这样接收方就收到了粘包数据。
- 接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接缓冲区,用户进程从该缓冲区取数据,若下一个数据包到达时,上一个数据包尚未被用户进程取走,则系统可能把多条数据当成是一条数据进行截取
黏包的现象
黏包现象一: 在发送端,由于在缓冲区两个数据小,发送的时间隔短,TCP会根据优化算法把这些数据合成一个发送 黏包现象二: 在接收端,由于在缓冲区没及时接受数据,截取数据时把多次发送的数据截取成一条,形成了黏包
client
# ### 客户端
import socket
import time
sk = socket.socket()
sk.connect( ("127.0.0.1",9001) )
# 在接收端,由于在缓冲区没及时接受数据,截取数据时把多次发送的数据截取成一条,形成了黏包
time.sleep(2)
# 收发数据的逻辑
res1 = sk.recv(1024)
print(res1.decode() , "<==1===>")
res2 = sk.recv(1024)
print(res2.decode() , "<==2===>")
sk.close()
server
# ### 服务端
import socket
import time
"""
黏包现象:
(1)发送端,数据小,时间间隔短,造成黏包
(2)接收端,没有及时接受数据,可能把多次发送的数据当成一条截取.
"""
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind( ("127.0.0.1",9001) )
sk.listen()
conn,addr = sk.accept()
# 收发数据的逻辑
conn.send("world,".encode())
# 在发送端,由于在缓冲区两个数据小,发送的时间隔短,TCP会根据优化算法把这些数据合成一个发送
# 解决代码 time.sleep(1) 解决发送优化为一个包
conn.send("hello".encode())
conn.close()
sk.close()
Struct
"""
pack 打包
把任意长度数字转换成具有固定4个字节长度的字节流
unpack 解包
把4个字节长度的值恢复成原来的数字,返回元组
"""范围: -21亿~21亿左右 控制在1.8G之内"""
"""
# pack
# i => int 要转换的当前类型是整型
"""范围: -21亿~21亿左右 控制在1.8G之内"""
res = struct.pack("i" , 999999998)
print(res , len(res)) # 4字节
res = struct.pack("i" , 1111111119)
print(res , len(res)) # 4字节
res = struct.pack("i" , 3)
print(res , len(res)) # 4字节
res = struct.pack("i" , 2000000000)
print(res , len(res))
# unpack
# i => 把对应的数据转化成整型
tup = struct.unpack("i" , res)
print(tup) #(2000000000,)
print(tup[0])
利用Struct解决方案
"""
解决黏包场景:
应用场景在实时通讯时,需要阅读此次发的消息是什么
不需要解决黏包场景:
下载或者上传文件的时候,最后要把包都结合在一起,黏包无所谓
"""
客户端
# ### 客户端
import socket
import time
import struct
sk = socket.socket()
sk.connect( ("127.0.0.1",9001) )
# 收发数据的逻辑
# 第一次接受数据 (数据长度)
num = sk.recv(4) # 固定struct的四个长度
tup = struct.unpack("i",num)
print(tup[0])
# 第二次接受数据 (真实数据)
res = sk.recv(tup[0]) # 以真实长度设置缓冲区
print(res.decode())
# 第三次接受数据 (真实数据)
res = sk.recv(1024)
print(res.decode())
sk.close()
服务端
# ### 服务端
import socket
import time
import struct
"""
黏包现象:
(1)发送端,数据小,时间间隔短,造成黏包
(2)接收端,没有及时接受数据,可能把多次发送的数据当成一条截取.
"""
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind( ("127.0.0.1",9001) )
sk.listen()
conn,addr = sk.accept()
# 收发数据的逻辑
strvar = input("[服务端]请输入您要发送的数据")
msg = strvar.encode()
# 准备Msg的数据长度封装
length = len(msg)
res = struct.pack("i",length)
# 第一次发送数据 (数据长度)
conn.send(res) # 以固定4B传递数据的大小
# 第二次发送数据 (真实数据)
conn.send(msg)
# 第三次发送数据 (真实数据)
conn.send(b"world,hello")
conn.close()
sk.close()

浙公网安备 33010602011771号