黏包

一、黏包现象

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

  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. layui 3种导航栏

    <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html ...

  2. MyEclipse下自定义(支持html5的)JSP模板--JSP

    需求:由于某些Mclipse版本发行的比较早,所以在有些版本(比如Mclipse2014,至于其他版本博主不知能不能创建html5格式的JSP页面)里创建JSP页面时html部分不是html5格式的. ...

  3. 自己动手编写IPv4地址包含关系测试的小脚本

    工作中需要对地址包含关系进行测试,现有ipaddress标准库和IPy无法满足,于是自己动手编写小脚本,主要实现== , in, <等专用功能,其他功能可以后续用到再补充,例如迭代打印网段内所有 ...

  4. yarn基础解释

    https://yarnpkg.com/zh-Hans/docs Yarn 对你的代码来说是一个包管理器, 你可以通过它使用全世界开发者的代码,或者分享自己的代码. 代码通过包(package)(或者 ...

  5. 机器学习ML策略

    1.为什么是ML策略 例如:识别cat分类器的识别率是90%,怎么进一步提高识别率呢? 想法: (1)收集更多数据 (2)收集更多的多样性训练样本 (3)使用梯度下降训练更长时间 (4)尝试Adam代 ...

  6. Luffy之登录认证以及JWT

    1.用户认证 在前面我们已经完成了,前端登录页面的搭建,以及路由分配,现在我们作关于登录认证部分的东西 Django提供了认证系统.认证系统包含: 用户 权限:二元(是/否)标志指示一个用户是否可以做 ...

  7. summary ranges leetcode java

    问题描述: Given a sorted integer array without duplicates, return the summary of its ranges. For example ...

  8. 『MXNet』第四弹_Gluon自定义层

    一.不含参数层 通过继承Block自定义了一个将输入减掉均值的层:CenteredLayer类,并将层的计算放在forward函数里, from mxnet import nd, gluon from ...

  9. Matlab-11:Gausssidel迭代法工具箱

    算法推导: function [u,n]=GaussSeid(A,b,u0,eps,M) %GaussSeid.m为用高斯-塞德尔迭代法求解线性方程组 %A为线性方程组的系数矩阵 %b为线性方程组的常 ...

  10. leetcode-algorithms-36 Valid Sudoku

    leetcode-algorithms-36 Valid Sudoku Determine if a 9x9 Sudoku board is valid. Only the filled cells ...