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