黏包

一、黏包现象

  同时执行多条命令之后,得到的结果很可能只有一部分,在执行其他命令的时候又接收到之前执行的另外一部分结果,这种显现就是黏包。

  server端

import socket

sk = socket.socket()
sk.bind(('127.0.0.1', 9000))
sk.listen() conn, addr = sk.accept()
conn.send(b'hello,')
conn.send(b'world')
conn.close()

  client端

import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 9000)) ret1 = sk.recv(1024)
print(ret1, len(ret1)) # b'hello,world' 11
ret2 = sk.recv(1024)
print(ret2, len(ret2)) # b'' 0
sk.close()

  注意:只有TCP有粘包现象,UDP永远不会粘包

二、黏包成因

1、合包现象:

  数据很短

  时间间隔短

2、拆包现象:

  大数据会发生拆分

  不会一次性的全部发送到对方

  对方在接收的时候很可能没用办法一次性接收到所有的信息

  那么没有接收完的信息很可能和后面的信息黏在一起

3、黏包现象只发生在tcp协议

  tcp协议的传输是:流式传输

  每一条信息与信息之间是没有边界

4、udp协议中是不会发生黏包现象的

  适合短数据的发送

  不建议发送过长的数据

  会增大数据丢失的几率

会发生黏包的两种情况:

1、发送方的缓存机制

  发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

2、接收方的缓存机制

  接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

总结:

 黏包现象只发生在tcp协议中:

  1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。

  2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

三、黏包的解决方案

1、解决方案:

  问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。

  server端:

import socket

sk = socket.socket()
sk.bind(('127.0.0.1', 9000))
sk.listen() conn, addr = sk.accept()
inp = input('>>>')
s = str(len(inp)).zfill(4) + inp # 在输入的内容前面拼上它的长度(4位数,不足在前面用0补齐)
conn.send(s.encode('utf-8')) conn.close()
'''
两个弊端:
复杂
最多也只能一次性传递9999个字节
'''

  client端:

import socket

sk = socket.socket()
sk.connect(('127.0.0.1', 9000)) ret1 = sk.recv(4) # 先接收前面的长度
num1 = int(ret1.decode('utf-8'))
ret = sk.recv(num1) # 再以长度接收
print(ret.decode('utf-8')) sk.close()
存在的问题:
程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗

2、解决方案进阶

  我们可以借助一个模块,这个模块可以把要发送的数据长度转换成固定长度的字节。这样客户端每次接收消息之前只要先接受这个固定长度字节的内容看一看接下来要接收的信息大小,那么最终接受的数据只要达到这个值就停止,就能刚好不多不少的接收完整的数据了。

struct模块:

  该模块可以把一个类型,如数字,转成固定长度的bytes

print(struct.pack('i',1111111111111))

struct.error: 'i' format requires -2147483648 <= number <= 2147483647 #这个是范围

  借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节数字。因此可以利用这个特点来预先发送数据长度。

发送时 接收时
先发送struct转换好的数据长度4字节 先接受4个字节使用struct转换成数字来获取要接收的数据长度
再发送数据 再按照长度接收数据

server端:

import struct
import socket sk = socket.socket()
sk.bind(('127.0.0.1', 9000))
sk.listen() conn, addr = sk.accept()
while 1:
s = input('>>>').encode('utf-8')
pack_num = struct.pack('i', len(s))
conn.send(pack_num)
conn.send(s)
conn.close()

client端:

import socket
import struct sk = socket.socket()
sk.connect(('127.0.0.1', 9000)) while 1:
pack_num = sk.recv(4)
num = struct.unpack('i', pack_num)[0]
ret = sk.recv(num)
print(ret.decode('utf-8'))
sk.close()

《Python》网络编程之黏包的更多相关文章

  1. Python学习笔记【第十四篇】:Python网络编程二黏包问题、socketserver、验证合法性

    TCP/IP网络通讯粘包问题 案例:模拟执行shell命令,服务器返回相应的类容.发送指令的客户端容错率暂无考虑,按照正确的指令发送即可. 服务端代码 # -*- coding: utf- -*- # ...

  2. Python网络编程之黏包问题

    二.解决黏包问题 2.1 解决黏包方法1 计算消息实体的大小 服务端接受两次,一次时消息大小,二次是消息实体,解决消息实体黏包 客户端发送两次,一次是消息大小,一次是消息实体 在两次收发之间加入一次多 ...

  3. python socket编程和黏包问题

    一.基于TCP的socket tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端,有顺序,不重复,可靠.不会被加上数据边界. server端 import socket sk = so ...

  4. 网络编程- 解决黏包现象方案二之struct模块(七)

    上面利用struct模块与方案一比较,减少一次发送和接收请求,因为方案一无法知道client端发送内容的长度到底有多长需要和接收OK.多一次请求防止黏包,减少网络延迟

  5. Python网络编程,粘包、分包问题的解决

    tcp编程中的粘包.分包问题的解决: 参考:https://blog.csdn.net/yannanxiu/article/details/52096465 服务端: #!/bin/env pytho ...

  6. python网络编程-socket“粘包”(小数据发送问题)

    一:什么是粘包 “粘包”, 即服务器端你调用时send 2次,但你send调用时,数据其实并没有立刻被发送给客户端,而是放到了系统的socket发送缓冲区里,等缓冲区满了.或者数据等待超时了,数据才会 ...

  7. python网络编程之粘包

    一.什么是粘包 须知:只有TCP有粘包现象,UDP永远不会粘包 粘包不一定会发生 如果发生了:1.可能是在客户端已经粘了 2.客户端没有粘,可能是在服务端粘了 首先需要掌握一个socket收发消息的原 ...

  8. Python网络编程与并发编程

    网络编程基础 黏包 , 并发 计算机网络的发展及基础网络概念 Python 中的进程与 锁 Python IO 多路复用 \协程

  9. Python之路 - 网络编程之粘包

    Python之路 - 网络编程之粘包 粘包

随机推荐

  1. 质控工具之cutadapt的使用方法

    cutadapt 参考:用cutadapt软件来对双端测序数据去除接头 fastqc可以用于检测,检测出来了怎么办? 看了几篇高水平文章,有不少再用cutadapt,虽然有时候数据真的不错,但是还是要 ...

  2. Django用户认证组件

    用户认证 主要分两部分: 1.auth模块   from django.contrib import auth 2.User对象 from django.contrib.auth.models imp ...

  3. html标签积累

    <marquee>滚动标签 <marquee>标签,它是成对出现的标签,首标签<marquee>和尾标签</marquee>之间的内容就是滚动内容.&l ...

  4. MyBatis中mybatis-generator代码生成的一般过程

    MyBatis框架的使用,可以参考我的文章: https://blog.csdn.net/JayInnn/article/details/81746571(基于Mybatis实现一个查库的接口) ht ...

  5. 如何开发mis系统--整理

    1.含义: 所谓MIS(管理信息系统--Management Information System)系统,主要指的是进行日常事务操作的系统.这种系统主要用于管理需要的记录,并对记录数据进行相关处理,将 ...

  6. java中堆与栈的区别

    堆与栈都是java中常用的存储结构,是内存中存放数据的地方. 堆:主要存放运行时创建(new)的对象.主要用于储存对象,存取速度慢,可以运行时动态分配内存,生命周期不需要提前确定. 栈:主要存放基础类 ...

  7. Guarding Bananas

    Guarding Bananas Once there was a lazy monkey in a forest. But he loved banana too much. One day the ...

  8. 微信小程序自动定位,通过百度地图根据经纬度获取该地点所在城市信息

    微信小程序获得经纬度 var that = this wx.getLocation({ type: 'wgs84', success(res) { console.log(res) that.setD ...

  9. php 异常捕获的坑

    thinkphp 框架需要注意 书写为(Exception $e)将无效   需要写成 (\Exception $e) try { throw new \Exception("Error P ...

  10. vue 项目中的坑 在项目中遇到 持续更新ing

    1.vue2.0 不支持 v-html 后绑定的内容使用过滤,可是有时候过滤必须使用-----------解决:通过methods中定义方法 然后 v-html='myMethods(string)' ...