• Python学习笔记-Day31-黏包的原理及解决办法-struct模块


    内容大纲:

    一、黏包现象

    二、解决方法

    一、黏包现象

    1、合包现象:当一串数据很短且发送间隔很段的时候,在发送端缓存区会将这两串数据合为一串发送出去。

    2、拆包现象:当一串数据过长时,tcp会将这次发送的数据拆成几个包发送出去,可能会和后面的数据进行合包

    3、黏包现象只发生在tcp中:

    原因1:tcp传输 协议是面向流的,接收端可以选择一次性接收2个字节或3个字节,每一条信息与信息之间是没有边界的,应用看到的数据是一个整体或者说是一个流(stream)

    原因2:tcp在传输数据时,不存在对包大小的限制,如果这段数据比较长,会被分段发送,如果较短,可能会等待和下一个数据一起发送

    4、udp不会发生黏包现象:

    原因1:udp的传输是面向无连接的,是面向消息的,每个udp包中都有消息头,就是有了消息保护边界,接收端能区分出哪些是属于同一条消息,应用程序必须以消息为单位进行提取,不能一次提取任意字节。

    但是消息在传输过程中丢失的话接收端就无法提取

    所以,udp适合短数据的发送,过长数据会增大数据丢死的几率

    sendto函数最大能发送的数据长度是65535字节

    总结:会发生黏包现象的原因:

    1、发送方和接收方的缓存机制,tcp协议的面向流通信的特点

    2、收发数据的边界不清晰,接收数据端不知道一次性该接收多少数据

    二、解决方案

    struct模块:把一个类型,如数字,转化成固定长度的bytes类型

    import struct
    bnum = struct.pack('i',12345)
    print(bnum,len(bnum))  #  b'90x00x00' 4
    num = struct.unpack('i',bnum)
    print(num)  #  (12345,)
    print(num[0],type(num[0]))  #  12345 <class 'int'>

    在网络中的应用:

    server端:

    import struct
    import socket
    sk = socket.socket()
    ip_port = ('127.0.0.1',9000)
    sk.bind(('127.0.0.1',9001))
    sk.listen()
    conn, addr = sk.accept()
    while True:
        s = input('>>>').encode('utf-8')
        # 用pack将数据的长度转化为四字节的bytes类型
        s_len_byte = struct.pack('i',len(s))
        # 先发送bytes类型的数据的长度
        conn.send(s_len_byte)
        # 再发送数据
        conn.send(s)
    
        # 先接收数据的长度
        r_len_byte = conn.recv(4)
        # 将长度转化为int类型,unpack返回一个元组,我们要的数据在第一位
        r_len = struct.unpack('i',r_len_byte)[0]
        print(r_len_byte,r_len)
        # 再接收这个长度的数据
        recmsg = conn.recv(r_len)
        print(recmsg.decode('utf-8'))
    conn.close()
    sk.close()

    client端:

    import struct
    import socket
    ip_port = ('127.0.0.1',9000)
    sk = socket.socket()
    sk.connect(('127.0.0.1',9001))
    while True:
        # 接收数据
        r_len_byte = sk.recv(4)
        r_len = struct.unpack('i',r_len_byte)[0]
        print(r_len_byte,r_len)
        rmsg = sk.recv(r_len)
        print(rmsg.decode('utf-8'))
        # 发送数据
        s = input('>>>').encode('utf-8')
        s_len_byte = struct.pack('i',len(s))
        sk.send(s_len_byte)
        sk.send(s)
    sk.close()
  • 相关阅读:
    格式化输出数字
    传教士经验
    集合
    替换
    连接
    填充
    取值
    分割
    创建日历和日期列表
    常用日期格式
  • 原文地址:https://www.cnblogs.com/tian-tian/p/9650196.html
Copyright © 2020-2023  润新知