黏包

一、黏包现象

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

  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. lua --- 函数的可变参数

    主要掌握: 1>虚变量 --- 一个下划线 2>lua将函数的可变参数放在一个叫 arg 的表中,除了参数以外,arg表中还有一个域n表示参数的个数. do function fun(x, ...

  2. 第 5 章 网络 - 032 - 学容器必须懂 bridge 网络

    bridge 网络 Docker 安装时会创建一个 命名为 docker0 的 linux bridge.如果不指定--network,创建的容器默认都会挂到 docker0 上. 创建一个容器 一个 ...

  3. google搜索小技巧

    google搜索小技巧 一.总结 一句话总结:But most people may not be using Google search to its full potential.Want to ...

  4. Getting started with Processing 第十三章——延伸(1)

    导入库: 导入库的名称为:import processing.libName.* 声音 播放声音 支持的格式:wav,aiff,mp3声明: SoundFile blip;创建:blip = new ...

  5. SSH免密钥登陆

    local ipaddress:10.47.39.7:remote ipaddress:10.47.39.8 1.生成公钥和私钥 [root@local ~]# ssh-keygen -t rsa  ...

  6. 20171023xlVBA递归统计WORD字数

    Dim dFilePath As Object, OneKey Sub main_proc() Dim Wb As Workbook, Sht As Worksheet, Rng As Range S ...

  7. Calendar Game HDU - 1079

    Adam and Eve enter this year’s ACM International Collegiate Programming Contest. Last night, they pl ...

  8. php的符号的排序大小

  9. IDEA能运行,但是出现红色下划线的问题报 cannot resolve method

    能编译通过并运行说明SDK导入正确,但是为啥我们点击每一个Java文件会出现好多红色的下划线 ,并提示idea cant resolve symbol.原因就是可能没有清除原来的历史缓存,导致一些错误 ...

  10. 【oauth2.0】【1】简单介绍

    含义: OAuth是一个关于授权(authorization)的开放网络标准,2.0是当前版本.不是技术,而是一项资源授权协议. OAuth在"客户端"与"服务提供商&q ...