
TCP黏包
发布日期:2021-05-09 05:58:46
浏览次数:10
分类:博客文章
本文共 3076 字,大约阅读时间需要 10 分钟。
黏包现象
在使用TCP协议进行数据传输的时候,会有以下问题出现。
client:
import socketsk = socket.socket()sk.connect(("127.0.0.1", 8101))# 连续发送数据s = "我爱你"sk.send(s.encode("utf-8"))sk.send(s.encode("utf-8"))print("发送完毕")sk.close()
server:
import socketsk = socket.socket()sk.bind(("127.0.0.1", 8101))sk.listen()conn, addr = sk.accept()msg1 = conn.recv(1024)print(msg1.decode("utf-8"))msg2 = conn.recv(1024)print(msg2.decode("utf-8"))sk.close()
运行结果
我们发现,打印来的效果是两个数据包合在一起了。为什么会这样呢? 在数据传输的时候客户端发送两次数据。这两个数据并不是直接发送出去的。首先会存放在缓冲区。等缓冲区数据装满或者经过一段时间后。会把缓冲区中的数据一起发送出去。 这就导致了一个很坑的现象。明明是两次发送的数据。被合在了一起。这就是典型的黏包现象。
注意,黏包现象只有TCP才会出现。UDP是不会出现黏包的。因为UDP的不连续性。每次发送的数据都会立刻打包成数据包然后发出去。数据包与数据包之间是有边界隔离的。你可以认为是一个sendto对应一个recvfrom。因此UDP不会出现黏包.
如何解决黏包问题
很简单。之所以出现黏包就是因为数据没有边界,直接把两个包混合成了一个包。那么我可以在发送数据的时候,指定边界,告诉对方,我接下来这个数据包有多大。 对面接收数据的时候呢,先读取该数据包的大小,然后再读取数据,就不会产生黏包了。
通俗的说,发送数据的时候制定数据的格式:长度+数据。 接收的时候就知道有多少是当前这个数据包的大小了。也就相当于定义了分隔边界了。
client:
import socketsk = socket.socket()sk.connect(("127.0.0.1", 8101))# 连续发送数据s = "我爱你"bs = s.encode("utf-8")# 计算数据长度. 格式化成4位数字bs_len = format(len(bs), "04d").encode("utf-8")# 发送数据之前. 先发送长度# 整个数据包: 0009\x\x\x\x\x\x...sk.send(bs_len)sk.send(bs)sk.send(bs_len)sk.send(bs)print("发送完毕")sk.close()
server:
import socketsk = socket.socket()sk.bind(("127.0.0.1", 8101))sk.listen()conn, addr = sk.accept()# 整个数据包: 0009\x\x\x\x\x\x...# 接收4个字节. 转换成数字bs_len = int(conn.recv(4).decode('Utf-8'))# 读取数据msg1 = conn.recv(bs_len)print(msg1.decode("utf-8"))bs_len = int(conn.recv(4).decode('Utf-8'))msg2 = conn.recv(bs_len)print(msg2.decode("utf-8"))sk.close()
如果每次发送数据都要经过这么一次,属实有点儿累。没关系,python提供了一个很好用的模块来帮我们解决这个恶心的问题
ret = struct.pack("i", 123456789)print(ret)print(len(ret)) # 4 不论数字大小, 定死了4个字节# 把字节还原回数字bs = b'\x15\xcd[\x07'num = struct.unpack("i", bs)[0]print(num)
优雅的解决黏包问题
client:
import socketimport structsk = socket.socket()sk.connect(("127.0.0.1", 8123))msg_bs = "我爱你".encode("utf-8")msg_struct_len = struct.pack("i", len(msg_bs))# 发一次sk.send(msg_struct_len)sk.send(msg_bs)# 发两次sk.send(msg_struct_len)sk.send(msg_bs)
server:
import socketimport structsk = socket.socket()sk.bind(("127.0.0.1", 8123))sk.listen()conn, addr = sk.accept()# 接收一个数据包msg_struct_len = conn.recv(4)msg_len = struct.unpack("i", msg_struct_len)[0]data = conn.recv(msg_len)print(data.decode('utf-8'))# 接收第二个数据包msg_struct_len = conn.recv(4)msg_len = struct.unpack("i", msg_struct_len)[0]data = conn.recv(msg_len)print(data.decode('utf-8'))
看着还是别扭?提取一个模块试试看
my_socket_util
import structdef my_send(sk, msg): msg_bs = msg.encode("utf-8") msg_struct_len = struct.pack("i", len(msg_bs)) sk.send(msg_struct_len) sk.send(msg_bs)def my_recv(sk): # 接收一个数据包 msg_struct_len = sk.recv(4) msg_len = struct.unpack("i", msg_struct_len)[0] data = sk.recv(msg_len) return data.decode("utf-8")
client:
import socketimport my_socket_util as msusk = socket.socket()sk.connect(("127.0.0.1", 8123))msu.my_send(sk, "我爱你")msu.my_send(sk, "我爱你")
server:
import socketimport my_socket_util as msusk = socket.socket()sk.bind(("127.0.0.1", 8123))sk.listen()conn, addr = sk.accept()print(msu.my_recv(conn))print(msu.my_recv(conn))
发表评论
最新留言
第一次来,支持一个
[***.219.124.196]2025年04月18日 16时31分58秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
POD类型
2021-05-09
Head First设计模式——迭代器模式
2021-05-09
记一次讲故事机器人的开发-我有故事,让机器人来读
2021-05-09
netcore中使用session
2021-05-09
【wp】HWS计划2021硬件安全冬令营线上选拔赛
2021-05-09
Ef+T4模板实现代码快速生成器
2021-05-09
Java面试题:Servlet是线程安全的吗?
2021-05-09
Linux探测工具BCC(可观测性)
2021-05-09
采坑 - 字符串的 "" 与 pd.isnull()
2021-05-09
《我是猫》总结
2021-05-09
mcrypt加密以及解密过程
2021-05-09
go等待N个线程完成操作总结
2021-05-09
Python 之网络式编程
2021-05-09
SpringCloud微服务(03):Hystrix组件,实现服务熔断
2021-05-09
网站故障公告1:使用阿里云RDS之后一个让人欲哭无泪的下午
2021-05-09
[网站公告]又拍云API故障造成图片无法上传(已恢复)
2021-05-09
上周热点回顾(6.9-6.15)
2021-05-09
上周热点回顾(5.9-5.15)
2021-05-09
上周热点回顾(1.23-1.29)
2021-05-09