038.Python关于TCP黏包问题
黏包现象
1 黏包现象演示
服务端
#服务端
import socket
sk = socket.socket()
# 注册主机到网络
sk.bind( ("127.0.0.1",9000) )
sk.listen()
# 三次握手
conn,addr = sk.accept()
# 收发数据的逻辑
# ...
conn.send("hello,".encode("utf-8")) #发送第一次包
conn.send("world".encode("utf-8")) #发送第二次包
# 四次挥手
conn.close()
# 退回端口
sk.close()
客户端
import socket
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) ) # 收发数据的逻辑
print(sk.recv(10)) #接收第一次,每一个包的大小是10个字节 print(sk.recv(10)) #接收第二次,每一个包的大小是10个字节 sk.close()
执行
[root@node10 tcp]# python3 server.py
[root@node10 tcp]# python3 client.py
b'hello,worl' #发现第二个包,黏到第一个包了
b'd'
2 黏包现象原因分析
tcp协议在发送数据时,会出现黏包现象
- 数据粘包是因为在客户湍/服务器端都会有一个数据缓中区缓中区用来临时保存数据,为了保证能够完整的接收到数据,因此缓中区都会设置的比较大。
- 在收发效据频繁时,由于tcp传输消息的无边界,不清楚应该截取多少长度,导致客户端/服务器端,都有可能把多条数据当成是一条数据进行截取,造成黏包
黏包的两种情况
- 黏包现象一:在发送端,由于两个数据短,发送的时间隔较短,所以在发送端形成包
- 黏包现象二:在接收端,由于两个数据几乎同时被发送到对方的缓存中,所有在接收端形成了黏包
- 总结:发送端,包之间时间间隔短或者接收端,接受不及时,就会黏包,核心是因为tcp对数据无边界戳取,不会按照发送的顺序判断
3 解决1,发送两个包的时间设置时间间隔
服务端
#服务端
import socket
import time
sk = socket.socket()
# 注册主机到网络
sk.bind( ("127.0.0.1",9000) )
sk.listen() # 三次握手
conn,addr = sk.accept() # 收发数据的逻辑
# ...
conn.send("hello,".encode("utf-8"))
time.sleep(0.2)
conn.send("world".encode("utf-8")) # 四次挥手
conn.close()
# 退回端口
sk.close()
客户端
import socket
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) ) # 收发数据的逻辑
print(sk.recv(10)) print(sk.recv(10)) sk.close()
执行
[root@node10 tcp]# python3 server.py
[root@node10 tcp]# python3 client.py
b'hello,'
b'world'
数据正常
但是服务端把数据发送到缓冲区,是正常的,但是客户端处理数据太慢,也会导致出现粘包现象
服务端
#服务端
import socket
import time
sk = socket.socket()
# 注册主机到网络
sk.bind( ("127.0.0.1",9000) )
sk.listen() # 三次握手
conn,addr = sk.accept() # 收发数据的逻辑
# ...
conn.send("hello,".encode("utf-8"))
time.sleep(0.2)
conn.send("world".encode("utf-8")) # 四次挥手
conn.close()
# 退回端口
sk.close()
客户端
import socket
import time
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) )
time.sleep(0.5)
# 收发数据的逻辑
print(sk.recv(10)) print(sk.recv(10)) sk.close()
执行
[root@node10 tcp]# python3 server.py
[root@node10 tcp]# python3 client.py
b'hello,worl'
b'd'
又出现了粘包现象
解决黏包现象的方法:让发送端不要发送时间间隔慢一点,同时让接收端接收数据不要延迟太久,但是这种方式不是根本解决方式
根本原因是tcp再接收数据时是没有边界的不知道截取多少个长度时第一次发送过来的数据,但是udp不会发生黏包现象,是因为UDP再发送数据,会首先计算一个数据的长度,是多少就发送多少
4 解决2,计算发送长度
服务端
#服务端
import socket
sk = socket.socket()
# 注册主机到网络
sk.bind( ("127.0.0.1",9000) )
sk.listen() # 三次握手
conn,addr = sk.accept() # 收发数据的逻辑
# ...
conn.send("".encode("utf8")) #计算出第一个包的长度是6字节,把这个数值发送给客户端
conn.send("hello,".encode("utf-8"))
conn.send("world".encode("utf-8")) # 四次挥手
conn.close()
# 退回端口
sk.close()
客户端
import socket
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) )
n = int(sk.recv(1).decode("utf-8")) #接收服务端发送过来的字节长度,强制转换为整型
# 收发数据的逻辑
print(sk.recv(n)) print(sk.recv(10)) sk.close()
执行
[root@node10 tcp]# python3 server.py
[root@node10 tcp]# python3 client.py
b'hello,'
b'world'
因为tcp 无边界的特征: 按照recv后面括号里面的值来进行接受数据,完全有可能在客户端 因为延迟问题,导致两次发送的数据黏在一起,因为tcp无边界,按照实际10个字节进行截图,根本分不清是第几次发送的. 从而黏包.
解决的方式:告诉客户端,直接你要截取的字节数是多少,按照send发送的实际字节数,进行截取.
当发送文件的字节过大,需要不停的修改接收端的值
5 解决3,使用8字节固定传输
#服务端
import socket
sk = socket.socket()
# 注册主机到网络
sk.bind( ("127.0.0.1",9000) )
sk.listen() # 三次握手
conn,addr = sk.accept() # 收发数据的逻辑
# ...
conn.send("".encode("utf8")) #计算出第一个包的长度是8位写法的字节长度,把这个数值发送给客户端
msg = "hello," * 20
conn.send(msg.encode("utf-8"))
conn.send("world".encode("utf-8")) # 四次挥手
conn.close()
# 退回端口
sk.close()
客户端
import socket
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) )
n = int(sk.recv(8).decode("utf-8")) #接收服务端发送过来的字节长度,固定8字节,强制转换为整型
# 收发数据的逻辑
print(sk.recv(n)) print(sk.recv(10)) sk.close()
执行
[root@node10 tcp]# python3 server.py
[root@node10 tcp]# python3 client.py
b'hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,'
b'world'
使用8个字节进行固定发送的时,可以解决平常大多数问题,但是没有从根本解决所有数据能都发送接收的情况.长度上还是受限制.
6 struct模块
- pack 把任意长度的数字转化成具有固定长度的4个字节的值,组成字节流
- unpack 把4个字节的值恢复成原有数据,最终返回的是一个元组
i => int 要转化的这个数据类型是整型
res = struct.pack("i",130000000)
print(res)
print(len(res))
res = struct.unpack("i",res)
print(res)
执行
[root@node10 python]# python3 test.py
b'\x80\xa4\xbf\x07'
4
(130000000,)
7 使用struct解决黏包现象
服务端
#服务端
import socket
import struct
sk = socket.socket()
# 注册主机到网络
sk.bind( ("127.0.0.1",9000) )
sk.listen() # 三次握手
conn,addr = sk.accept() # 收发数据的逻辑
inp = input ("please input your vaule>>>>:")
msg = inp.encode("utf-8")
#使用pack方法,直接算出4个字节长度的二进制字节流
res = struct.pack("i",len(msg)) conn.send(res)
conn.send(msg)
conn.send("world".encode("utf-8")) # 四次挥手
conn.close()
# 退回端口
sk.close()
客户端
import socket
import struct
sk = socket.socket()
sk.connect( ("127.0.0.1",9000) )
#发送的已经固定的4个字节
n = sk.recv(4)
#获取元组中的第一个值,因为返回的是元组
n = struct.unpack("i",n)[0]
# 收发数据的逻辑
print(sk.recv(n)) print(sk.recv(10)) sk.close()
执行
[root@node10 tcp]# python3 server.py
please input your vaule>>>>:cdhaueqfebca ferngcdoascj
[root@node10 tcp]# python3 client.py
b'cdhaueqfebca ferngcdoascj'
b'world'
粘包问题解决
038.Python关于TCP黏包问题的更多相关文章
- Python网络编程基础 ❷ 基于upd的socket服务 TCP黏包现象
TCP的长连接 基于upd的socket服务 TCP黏包现象
- netty]--最通用TCP黏包解决方案
netty]--最通用TCP黏包解决方案:LengthFieldBasedFrameDecoder和LengthFieldPrepender 2017年02月19日 15:02:11 惜暮 阅读数:1 ...
- socketserver tcp黏包
socket (套接字) tcp(黏包现象原因) 传输中由于内核区缓冲机制(等待时间,文件大小),会在 发送端 缓冲区合并连续send的数据,也会出现在 接收端 缓冲区合并recv的数据给指定port ...
- python tcp黏包和struct模块解决方法,大文件传输方法及MD5校验
一.TCP协议 粘包现象 和解决方案 黏包现象让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd)执行远程命令的模块 需要用到模块subprocess sub ...
- Python Socket通信黏包问题分析及解决方法
参考:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 1.黏包的表现(以客户端远程操作服务端命令为例) 注:只有在TCP协议通信的 ...
- TCP黏包问题
什么是黏包?什么情况下会出现黏包的情况?该如何避免黏包的情况? 首先来看一个例子 #服务端 import time from socket import * server = socket(AF_IN ...
- python基础(30):黏包、socket的其他方法
1. 黏包 1.1 黏包现象 让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd) 同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接 ...
- tcp黏包问题与udp为什么不黏包
1.先说下subprocess模块的用法,为了举个黏包的例子 # 通过一个例子 来认识网络编程中的一个重要的概念 # 所有的客户端执行server端下发的指令,执行完毕后,客户端将执行结果给返回给服务 ...
- Linux tcp黏包解决方案
tcpip协议使用"流式"(套接字)进行数据的传输,就是说它保证数据的可达以及数据抵达的顺序,但并不保证数据是否在你接收的时候就到达,特别是为了提高效率,充分利用带宽,底层会使用缓 ...
随机推荐
- Neo4j集群容器化部署
集群基本配置(示例) core servers: 10.110.10.11, 10.110.10.12, 10.110.10.13read replicas: 10.110.10.14, 10.110 ...
- C++Primer第五版 6.1节练习
练习6.1:实参和形参的区别是什么? 通俗解释: 实参是形参的初始值.编译器能以任意可行的顺序对实参求值.实参的类型必须与对应的形参类型匹配. 详解1) 形参变量只有在函数被调用时才会分配内存,调用结 ...
- 【LC_Lesson7】---将两个有序链表合成新的一个有序链表
将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1->1->2- ...
- JMeter——jmx脚本文件解析
<!--Jmeter版本信息--> <?xml version="1.0" encoding="UTF-8"?> <jmeterT ...
- Nginx代理服务——常用的配置语法
可以到官方查看所有代理的配置语法http://nginx.org/en/docs/http/ngx_http_proxy_module.html 缓存区 Syntax:proxy_buffering ...
- Spring Boot自动装配
前言 一些朋友问我怎么读源码,这篇文章结合我看源码时候一些思路给大家聊聊,我主要从这三个方向出发: 确定目标,这个目标要是一个具体,不要一上来我要看懂Spring,这是不可能的,目标要这么来定,比如看 ...
- css 透明度
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8& ...
- [DP][SA][可持久化线段树]黑红兔
源自 xyz32768 菜鸡的 FJ 省冬令营模拟赛题 原题 CF1063F Statement 给定一个长度为 \(n\) 的字符串 \(s\),仅包含小写英文字母 要从中从左往右选出若干段不相交的 ...
- sas9.2 windows7系统 10年11月后 建立永久数据集时,提示:“用户没有与逻辑库相应的授权级别
先把你这个逻辑库删掉,在桌面创立空的新文件夹,然后用这个新文件夹在sas里新建逻辑库,名字照旧,再把你要的数据放进空文件夹就好了
- linux pycharm 安装
Ubuntu安装之python开发 什么??公司要用Ubuntu(乌班图)?不会用??怎么进行python开发??? 乌班图操作系统下载地址:http://releases.ubuntu.com/ ...