# tcp协议在发送数据时,会出现黏包现象.
(1)数据粘包是因为在客户端/服务器的发送端和接收端都会有一个数据缓冲区,
缓冲区用来临时保存数据,默认空间都设置较大。在收发数据频繁时,由于tcp传输消息的无边界特点,不清楚应该截取多少长度,导致客户端/服务器端,都有可能把多条数据当成是一条数据进行截取,造成黏包
(2)发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个数据包。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后在发送,这样接收方就收到了粘包数据。
(3)接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接缓冲区,用户进程从该缓冲区取数据,若下一个数据包到达时,上一个数据包尚未被用户进程取走,则系统可能把多条数据当成是一条数据进行截取
# 总结: TCP协议是面向连接的无边界协议
黏包现象一:
在发送端,由于在缓冲区两个数据小,发送的时间隔短,TCP会根据优化算法把这些数据合成一个发送
黏包现象二:
在接收端,由于在缓冲区没及时接受数据,截取数据时把多次发送的数据截取成一条,形成了黏包
#tcp协议:
缺点:接收时数据之间无边界,有可能粘合几条数据成一条数据,造成黏包
优点:不限制数据包的大小,稳定传输不丢包
#udp协议:
优点:接收时候数据之间有边界,传输速度快,不黏包
缺点:限制数据包的大小(受带宽路由器等因素影响),传输不稳定,可能丢包
#tcp和udp对于数据包来说都可以进行拆包和解包,理论上来讲,无论多大都能分次发送
但是tcp一旦发送失败,对方无响应(对方无回执),tcp可以选择再发,直到对应响应完毕为止
而udp一旦发送失败,是不会询问对方是否有响应的,如果数据量过大,易丢包
#解决黏包场景:
应用场景在实时通讯时,需要阅读此次发的消息是什么
#不需要解决黏包场景:
下载或者上传文件的时候,最后要把包都结合在一起,黏包无所谓.
(1)发送端,数据小,时间间隔短,造成黏包
(2)接收端,没有及时接受数据,可能把多次发送的数据当成一条截取.
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()View Code
import socket import time 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()) time.sleep(1) conn.send("hello".encode()) conn.close() sk.close()View Code
import socket import time sk = socket.socket() sk.connect( ("127.0.0.1",9001) ) time.sleep(2) # 收发数据的逻辑 # 第一步,先接受接下来要发送的数据的总大小 res = sk.recv(1) num = int(res.decode()) # 第二部,在接受真实的数据 res1 = sk.recv(num) print(res1.decode() , "<==1===>") res2 = sk.recv(1024) print(res2.decode() , "<==2===>") sk.close()View Code
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("6".encode()) # 第二部,发送真实的数据 conn.send("world,".encode()) conn.send("hello".encode()) conn.close() sk.close()View Code
import socket import time sk = socket.socket() sk.connect( ("127.0.0.1",9001) ) time.sleep(2) # 收发数据的逻辑 # 第一步,先接受接下来要发送的数据的总大小 res = sk.recv(8) num = int(res.decode()) # 180 # 第二部,在接受真实的数据 res1 = sk.recv(num) print(res1.decode() , "<==1===>") res2 = sk.recv(1024) print(res2.decode() , "<==2===>") sk.close()View Code
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("00000180".encode()) # 第二部,发送真实的数据 msg = "world," * 30 conn.send(msg.encode()) conn.send("hello".encode()) conn.close() sk.close()View Code
import socket import time import struct sk = socket.socket() sk.connect( ("127.0.0.1",9001) ) time.sleep(2) # 收发数据的逻辑 # 第一次接受数据 (数据长度) num = sk.recv(4) 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()View Code
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() length = len(msg) res = struct.pack("i",length) # 第一次发送数据 (数据长度) conn.send(res) # 第二次发送数据 (真实数据) conn.send(msg) # 第三次发送数据 (真实数据) conn.send(b"world,hello") conn.close() sk.close()View Code
解决黏包场景:
应用场景在实时通讯时,需要阅读此次发的消息是什么
不需要解决黏包场景:
下载或者上传文件的时候,最后要把包都结合在一起,黏包无所谓.
import struct """ pack 打包 把任意长度数字转换成具有固定4个字节长度的字节流 unpack 解包 把4个字节长度的值恢复成原来的数字,返回元组 """ # pack # i => int 要转换的当前类型是整型 """范围: -21亿~21亿左右 控制在1.8G之内""" res = struct.pack("i" , 999999998) print(res , len(res)) res = struct.pack("i" , 1111111119) print(res , len(res)) res = struct.pack("i" , 3) print(res , len(res)) res = struct.pack("i" , 2000000000) print(res , len(res)) # unpack # i => 把对应的数据转化成整型 tup = struct.unpack("i" , res) print(tup) #(2000000000,) print(tup[0])