黏包现象

1 黏包现象演示

服务端

  1. #服务端
  2. import socket
  3. sk = socket.socket()
  4. # 注册主机到网络
  5. sk.bind( ("127.0.0.1",9000) )
  6. sk.listen()
  7. # 三次握手
  8. conn,addr = sk.accept()
  9. # 收发数据的逻辑
  10. # ...
  11. conn.send("hello,".encode("utf-8")) #发送第一次包
  12. conn.send("world".encode("utf-8")) #发送第二次包
  13. # 四次挥手
  14. conn.close()
  15. # 退回端口
  16. sk.close()

客户端

  1. import socket
  2. sk = socket.socket()
  3. sk.connect( ("127.0.0.1",9000) )
  4.  
  5. # 收发数据的逻辑
  6. print(sk.recv(10)) #接收第一次,每一个包的大小是10个字节
  7.  
  8. print(sk.recv(10)) #接收第二次,每一个包的大小是10个字节
  9.  
  10. sk.close()

执行

  1. [root@node10 tcp]# python3 server.py
  2. [root@node10 tcp]# python3 client.py
  3. b'hello,worl' #发现第二个包,黏到第一个包了
  4. b'd'

2 黏包现象原因分析

tcp协议在发送数据时,会出现黏包现象

  1. 数据粘包是因为在客户湍/服务器端都会有一个数据缓中区缓中区用来临时保存数据,为了保证能够完整的接收到数据,因此缓中区都会设置的比较大。
  2. 在收发效据频繁时,由于tcp传输消息的无边界,不清楚应该截取多少长度,导致客户端/服务器端,都有可能把多条数据当成是一条数据进行截取,造成黏包

黏包的两种情况

  • 黏包现象一:在发送端,由于两个数据短,发送的时间隔较短,所以在发送端形成包
  • 黏包现象二:在接收端,由于两个数据几乎同时被发送到对方的缓存中,所有在接收端形成了黏包
  • 总结:发送端,包之间时间间隔短或者接收端,接受不及时,就会黏包,核心是因为tcp对数据无边界戳取,不会按照发送的顺序判断

3 解决1,发送两个包的时间设置时间间隔

服务端

  1. #服务端
  2. import socket
  3. import time
  4. sk = socket.socket()
  5. # 注册主机到网络
  6. sk.bind( ("127.0.0.1",9000) )
  7. sk.listen()
  8.  
  9. # 三次握手
  10. conn,addr = sk.accept()
  11.  
  12. # 收发数据的逻辑
  13. # ...
  14. conn.send("hello,".encode("utf-8"))
  15. time.sleep(0.2)
  16. conn.send("world".encode("utf-8"))
  17.  
  18. # 四次挥手
  19. conn.close()
  20. # 退回端口
  21. sk.close()

客户端

  1. import socket
  2. sk = socket.socket()
  3. sk.connect( ("127.0.0.1",9000) )
  4.  
  5. # 收发数据的逻辑
  6. print(sk.recv(10))
  7.  
  8. print(sk.recv(10))
  9.  
  10. sk.close()

执行

  1. [root@node10 tcp]# python3 server.py
  2. [root@node10 tcp]# python3 client.py
  3. b'hello,'
  4. b'world'

数据正常

但是服务端把数据发送到缓冲区,是正常的,但是客户端处理数据太慢,也会导致出现粘包现象

服务端

  1. #服务端
  2. import socket
  3. import time
  4. sk = socket.socket()
  5. # 注册主机到网络
  6. sk.bind( ("127.0.0.1",9000) )
  7. sk.listen()
  8.  
  9. # 三次握手
  10. conn,addr = sk.accept()
  11.  
  12. # 收发数据的逻辑
  13. # ...
  14. conn.send("hello,".encode("utf-8"))
  15. time.sleep(0.2)
  16. conn.send("world".encode("utf-8"))
  17.  
  18. # 四次挥手
  19. conn.close()
  20. # 退回端口
  21. sk.close()

客户端

  1. import socket
  2. import time
  3. sk = socket.socket()
  4. sk.connect( ("127.0.0.1",9000) )
  5. time.sleep(0.5)
  6. # 收发数据的逻辑
  7. print(sk.recv(10))
  8.  
  9. print(sk.recv(10))
  10.  
  11. sk.close()

执行

  1. [root@node10 tcp]# python3 server.py
  2. [root@node10 tcp]# python3 client.py
  3. b'hello,worl'
  4. b'd'

又出现了粘包现象

解决黏包现象的方法:让发送端不要发送时间间隔慢一点,同时让接收端接收数据不要延迟太久,但是这种方式不是根本解决方式

根本原因是tcp再接收数据时是没有边界的不知道截取多少个长度时第一次发送过来的数据,但是udp不会发生黏包现象,是因为UDP再发送数据,会首先计算一个数据的长度,是多少就发送多少

4 解决2,计算发送长度

服务端

  1. #服务端
  2. import socket
  3. sk = socket.socket()
  4. # 注册主机到网络
  5. sk.bind( ("127.0.0.1",9000) )
  6. sk.listen()
  7.  
  8. # 三次握手
  9. conn,addr = sk.accept()
  10.  
  11. # 收发数据的逻辑
  12. # ...
  13. conn.send("".encode("utf8")) #计算出第一个包的长度是6字节,把这个数值发送给客户端
  14. conn.send("hello,".encode("utf-8"))
  15. conn.send("world".encode("utf-8"))
  16.  
  17. # 四次挥手
  18. conn.close()
  19. # 退回端口
  20. sk.close()

客户端

  1. import socket
  2. sk = socket.socket()
  3. sk.connect( ("127.0.0.1",9000) )
  4. n = int(sk.recv(1).decode("utf-8")) #接收服务端发送过来的字节长度,强制转换为整型
  5. # 收发数据的逻辑
  6. print(sk.recv(n))
  7.  
  8. print(sk.recv(10))
  9.  
  10. sk.close()

执行

  1. [root@node10 tcp]# python3 server.py
  2. [root@node10 tcp]# python3 client.py
  3. b'hello,'
  4. b'world'

因为tcp 无边界的特征: 按照recv后面括号里面的值来进行接受数据,完全有可能在客户端 因为延迟问题,导致两次发送的数据黏在一起,因为tcp无边界,按照实际10个字节进行截图,根本分不清是第几次发送的. 从而黏包.

解决的方式:告诉客户端,直接你要截取的字节数是多少,按照send发送的实际字节数,进行截取.

当发送文件的字节过大,需要不停的修改接收端的值

5 解决3,使用8字节固定传输

  1. #服务端
  2. import socket
  3. sk = socket.socket()
  4. # 注册主机到网络
  5. sk.bind( ("127.0.0.1",9000) )
  6. sk.listen()
  7.  
  8. # 三次握手
  9. conn,addr = sk.accept()
  10.  
  11. # 收发数据的逻辑
  12. # ...
  13. conn.send("".encode("utf8")) #计算出第一个包的长度是8位写法的字节长度,把这个数值发送给客户端
  14. msg = "hello," * 20
  15. conn.send(msg.encode("utf-8"))
  16. conn.send("world".encode("utf-8"))
  17.  
  18. # 四次挥手
  19. conn.close()
  20. # 退回端口
  21. sk.close()

客户端

  1. import socket
  2. sk = socket.socket()
  3. sk.connect( ("127.0.0.1",9000) )
  4. n = int(sk.recv(8).decode("utf-8")) #接收服务端发送过来的字节长度,固定8字节,强制转换为整型
  5. # 收发数据的逻辑
  6. print(sk.recv(n))
  7.  
  8. print(sk.recv(10))
  9.  
  10. sk.close()

执行

  1. [root@node10 tcp]# python3 server.py
  2. [root@node10 tcp]# python3 client.py
  3. b'hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,hello,'
  4. b'world'

使用8个字节进行固定发送的时,可以解决平常大多数问题,但是没有从根本解决所有数据能都发送接收的情况.长度上还是受限制.

6 struct模块

  • pack 把任意长度的数字转化成具有固定长度的4个字节的值,组成字节流
  • unpack 把4个字节的值恢复成原有数据,最终返回的是一个元组

i => int 要转化的这个数据类型是整型

  1. res = struct.pack("i",130000000)
  2. print(res)
  3. print(len(res))
  4.  
  5. res = struct.unpack("i",res)
  6. print(res)

执行

  1. [root@node10 python]# python3 test.py
  2. b'\x80\xa4\xbf\x07'
  3. 4
  4. (130000000,)

7 使用struct解决黏包现象

服务端

  1. #服务端
  2. import socket
  3. import struct
  4. sk = socket.socket()
  5. # 注册主机到网络
  6. sk.bind( ("127.0.0.1",9000) )
  7. sk.listen()
  8.  
  9. # 三次握手
  10. conn,addr = sk.accept()
  11.  
  12. # 收发数据的逻辑
  13. inp = input ("please input your vaule>>>>:")
  14. msg = inp.encode("utf-8")
  15. #使用pack方法,直接算出4个字节长度的二进制字节流
  16. res = struct.pack("i",len(msg))
  17.  
  18. conn.send(res)
  19. conn.send(msg)
  20. conn.send("world".encode("utf-8"))
  21.  
  22. # 四次挥手
  23. conn.close()
  24. # 退回端口
  25. sk.close()

客户端

  1. import socket
  2. import struct
  3. sk = socket.socket()
  4. sk.connect( ("127.0.0.1",9000) )
  5. #发送的已经固定的4个字节
  6. n = sk.recv(4)
  7. #获取元组中的第一个值,因为返回的是元组
  8. n = struct.unpack("i",n)[0]
  9. # 收发数据的逻辑
  10. print(sk.recv(n))
  11.  
  12. print(sk.recv(10))
  13.  
  14. sk.close()

执行

  1. [root@node10 tcp]# python3 server.py
  2. please input your vaule>>>>:cdhaueqfebca ferngcdoascj
  3. [root@node10 tcp]# python3 client.py
  4. b'cdhaueqfebca ferngcdoascj'
  5. b'world'

粘包问题解决

038.Python关于TCP黏包问题的更多相关文章

  1. Python网络编程基础 ❷ 基于upd的socket服务 TCP黏包现象

    TCP的长连接 基于upd的socket服务 TCP黏包现象

  2. netty]--最通用TCP黏包解决方案

    netty]--最通用TCP黏包解决方案:LengthFieldBasedFrameDecoder和LengthFieldPrepender 2017年02月19日 15:02:11 惜暮 阅读数:1 ...

  3. socketserver tcp黏包

    socket (套接字) tcp(黏包现象原因) 传输中由于内核区缓冲机制(等待时间,文件大小),会在 发送端 缓冲区合并连续send的数据,也会出现在 接收端 缓冲区合并recv的数据给指定port ...

  4. python tcp黏包和struct模块解决方法,大文件传输方法及MD5校验

    一.TCP协议 粘包现象 和解决方案 黏包现象让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd)执行远程命令的模块 需要用到模块subprocess sub ...

  5. Python Socket通信黏包问题分析及解决方法

    参考:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 1.黏包的表现(以客户端远程操作服务端命令为例) 注:只有在TCP协议通信的 ...

  6. TCP黏包问题

    什么是黏包?什么情况下会出现黏包的情况?该如何避免黏包的情况? 首先来看一个例子 #服务端 import time from socket import * server = socket(AF_IN ...

  7. python基础(30):黏包、socket的其他方法

    1. 黏包 1.1 黏包现象 让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd) 同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接 ...

  8. tcp黏包问题与udp为什么不黏包

    1.先说下subprocess模块的用法,为了举个黏包的例子 # 通过一个例子 来认识网络编程中的一个重要的概念 # 所有的客户端执行server端下发的指令,执行完毕后,客户端将执行结果给返回给服务 ...

  9. Linux tcp黏包解决方案

    tcpip协议使用"流式"(套接字)进行数据的传输,就是说它保证数据的可达以及数据抵达的顺序,但并不保证数据是否在你接收的时候就到达,特别是为了提高效率,充分利用带宽,底层会使用缓 ...

随机推荐

  1. PQSQL 按照时间进行分组

    按照时间分组时一般是按照年.月.日进行分组,不会把时分秒也算进去,所以需要把时间戳提取出所需要的时间段,本质上是把时间戳格式化成对应形式的字符串,这个过程需要用to_char(timestamp, t ...

  2. 基于 HTML5 + WebGL 的3D无人机 展示

    前言 近年来,无人机的发展越发迅速,既可民用于航拍,又可军用于侦察,涉及行业广泛,也被称为“会飞的照相机”.但作为军事使用,无人机的各项性能要求更加严格.重要.本系统则是通过 Hightopo 的   ...

  3. 通过例子进阶学习C++(五)计算2的1次方至2的64次方之和

    本文是通过例子学习C++的第五篇,通过这个例子可以快速入门c++相关的语法. 1.上篇回顾 在上一篇中,我们通过字符数组计算264次方: 通过例子进阶学习C++(四)计算2的64次方 带着这个问题:为 ...

  4. 第一篇:python中的判断语句和循环

    python与C语言的代码格式区别: 需注意:1.python中语句结束没有分号 “;” 2.python中严格要求缩进,且在判断和循环等语句中把括号用冒号代替. 3.经常使用tab键进行缩进. 4. ...

  5. TCP/IP协议与HTTP协议(一)

    1.什么是TCP/IP  如果要了解一个人,可以从他归属的集体聊起来.我们的HTTP协议就属于TCP/IP协议家族中的一员,了解HTTP协议再整个网络流程中的地位,也能更加充分的理解HTTP协议. 要 ...

  6. java 运行和编译

    一.编译->运行 1.编译 javac  文件名.java  生成 文件名.class 2.运行 java 文件名 注意:正在运行的是 文件名.class 因此:修改源文件后,要先编译,再运行 ...

  7. 2020寒假学习01 Scala 编程初级实践

    1. 计算级数请用脚本的方式编程计算并输出下列级数的前 n 项之和 Sn,直到 Sn 刚好大于或等于 q为止,其中 q 为大于 0 的整数,其值通过键盘输入. Sn = 2/1+3/2+4/3+... ...

  8. [洛谷P1606] [USACO07FEB] 荷叶塘Lilypad Pond

    Description 为了让奶牛们娱乐和锻炼,农夫约翰建造了一个美丽的池塘.这个长方形的池子被分成了M行N列个方格(1≤M,N≤30).一些格子是坚固得令人惊讶的莲花,还有一些格子是岩石,其余的只是 ...

  9. 电脑开机后多了OneKey Ghost启动选项怎么解决

    原文地址:http://www.xitongcheng.com/jiaocheng/dnrj_article_18745.html 大多数用户在使用OneKey Ghost安装电脑系统后,会在开机启动 ...

  10. 超越队西柚考勤系统——beta冲刺1

    这个作业属于哪个课程 http://edu.cnblogs.com/campus/xnsy/GeographicInformationScience 这个作业的要求在哪里 https://www.cn ...