• 网络编程之黏包


    一、黏包现象:

    一次只能接收到部分信息

     

    黏包原理:

    tcp:客户端每次发送数据给服务器端,服务器端都会给客户端一个回复

    tcp的合包机制:对tcp的原理做了优化,客户端短时间内发送了两条很短的信息,将两条信息合并为一条,但只返回一个回复

     

    tcp的拆包机制:

    网络上传输的字节数是有限的,网络上传输最大字节数是1500个字节,超过1500个字节就会拆包,拆成N个包分别发送过去,每收到一个包都会给回复,没收到的包就会再发送一次没收到的包,然后再合包

     

    内核的缓存机制,将很小的两个包合并。

    内存的缓存机制,包超过了1500个字节,会拆包后分别发送,另一端要求接收,没发送完先接收包

     

    造成黏包的实现原理:

    tcp合包现象:①数据很短 ②时间间隔短

    tcp拆包现象:大数据会发生拆分,不会一次性全部发送给对方,对方在接收的时候很可能没有一次性接收到所有的信息,那么没有接收完的信息很可能跟后面的信息黏在一起

     

    黏包现象只发生在tcp协议:tcp协议的传输是流式传输,每一条信息与信息之间式没有边界的,因为没有区分信息数目的机制

    udp协议中不会发送黏包现象:upd协议适合短数据的发送(接收端不会回复,而且有分界线的概念),udp发送大数据会拆包,另一端会合包,假如数据丢包,不会再重新发送。而且不能发送过长的数据(增大了数据丢失的几率)

     

    在程序中会出现黏包的原因:接收数据边界不清晰,即接收端不清楚接收数据的长度

    解决方案:在接收端限制接收的长度

     

    二、解决黏包方案

    解决黏包现象的方案1:

    自定义协议:接收端知道数据的长度,根据接收的长度来控制

    接收端先获取接收字节的长度,然后再根据长度取接收

    # server
    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',9090))
    sk.listen()
    conn,addr = sk.accept()
    num = conn.recv(2)          # 接收数据的长度
    print(num)                  # b'16'
    num = num.decode().zfill(4)
    msg = conn.recv(int(num))   # 根据数据的长度来接收
    print(msg.decode())         # character string
    # client
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',9090))
    
    sk.send(b'16character string')      # 在数据的前面定义数据的长度

    此方案的弊端:

    ① 复杂

    ② 最多只能一次传递9999个字节

     

    解决黏包现象的方案二:

    不管数据的长度是多少,都转化为4个字节,利用struct模块自定义协议。

    先统计bytes数据的长度,然后通过struct模块将数据长度转化为4个字节,接收端通过struct模块获得是一个元组,按0索引取值,还原数据的长度

     

    server端:统计数据的长度,通过struct模块,处理数据的长度,得到一个4个字节的结果,先发送4个字节的长度,再发送数据

    client端:先接收4个字节,通过struct来处理这4个字节,拿到数据的长度,按照数据的长度来接收数据

    # server端
    import socket
    import struct
    sk = socket.socket()
    sk.bind(('127.0.0.1',9091))
    sk.listen()
    
    conn,addr = sk.accept()
    num = conn.recv(4)                  # 接收数据的长度
    num = struct.unpack('i',num)        # 转化为原来数据的长度
    msg = conn.recv(num[0])             # 接收数据
    print(msg.decode())
    # client端
    import socket
    import struct
    sk = socket.socket()
    sk.connect(('127.0.0.1',9091))
    
    msg = 'asdfghjkl'.encode('utf-8')
    num = struct.pack('i',len(msg)) # 将数据长度转化为4个字节
    sk.send(num)                    # 发送数据长度
    sk.send(msg)                    # 发送数据

     

  • 相关阅读:
    NOIP 模拟 序列操作
    LUOGU 1525 关押罪犯
    HDU2473 Junk-Mail Filter
    BZOJ 2096 Pilots
    luogu 3939 数颜色
    NOIP模拟 赌博游戏
    Unity3D
    HTML5
    Cocos2d-x——支持多触点
    Cocos2d-x——Cocos2d-x 屏幕适配总结
  • 原文地址:https://www.cnblogs.com/st-st/p/9650095.html
Copyright © 2020-2023  润新知