zoukankan      html  css  js  c++  java
  • 黏包

    什么是黏包?

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

    tcp黏包

    服务器端
    import socket
    sk = socket.socket()
    
    sk.bind(('127.0.0.1',8888))
    sk.listen()
    
    conn,addr = sk.accept()
    while 1:
        conn.send(b'hello')
        conn.send(b'world')
    conn.close()
    sk.close()
    客户端
    import socket
    sk = socket.socket()
    
    sk.connect_ex(('127.0.0.1',8888))
    while 1 :
        msg1 = sk.recv(1024)
        print('msg1:',msg1)
    
        msg2 = sk.recv(1024)
    
        print('msg2:',msg2)
    
    sk.close()

    UDP黏包

    服务器
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(('127.0.0.1',8899))
    
    while 1:
        msg1 = sk.recvfrom(1024)
        print('msg1:', msg1)
    
        msg2 = sk.recvfrom(1024)
        print('msg2:', msg2)
    
    sk.close()
    客户端
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    
    while 1 :
        sk.sendto(b'hello',('127.0.0.1',8899))
        sk.sendto(b'world',('127.0.0.1',8899))
    
    sk.close()

    注意:只有TCP有粘包现象,UDP永远不会粘包

    黏包成因

    TCP协议中的数据传递

    tcp协议的拆包机制:

    当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去.MTU是Maximum Transmission Unit的缩写。意思是网络上传送的最大数据包。MTU的单位是字节。 大部分网络设备的MTU都是1500。如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据包碎片,增加丢包率,降低网络速度。

    面向流的通信特点和Nagle算法

    TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。
    收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。
    这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
    对于空消息:tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),也可以被发送,udp协议会帮你封装上消息头发送过去。
    可靠黏包的tcp协议:tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。

    UDP不会发生黏包

    UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。
    不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
    对于空消息:tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),也可以被发送,udp协议会帮你封装上消息头发送过去。
    不可靠不黏包的udp协议:udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成,若是y;x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠。

    补充说明:

    用UDP协议发送时,用sendto函数最大能发送数据的长度为:65535- IP头(20) – UDP头(8)=65507字节。用sendto函数发送数据时,如果发送数据长度大于该值,则函数会返回错误。(丢弃这个包,不进行发送)
        用TCP协议发送时,由于TCP是数据流协议,因此不存在包大小的限制(暂不考虑缓冲区的大小),这是指在用send函数时,数据长度参数不受限制。而实际上,所指定的这段数据并不一定会一次性发送出去,如果这段数据比较长,会被分段发送,如果比较短,可能会等待和下一次数据一起发送。

    总结:

    粘包问题 :  只有tcp协议才会发送粘包,udp不会发

        EX:  发送端发送数据,接收端不知道应该如何接收,造成的一种数据混乱的现象

               tcp协议中,

    一个合包机制(nagle算法),多次连续发送且间隔较小的数据,进行打包数据传送

    有一个机制是拆包机制,发送端,因为受到网卡的MTU限制,将大的超过MTU限制的数据,进行拆分,拆分成多个小的数据,进行传输.  传输到目标主机的操作系统层时,重新将多个小的数据合并成原本的数据

    针对 使用udp协议发送数据,一次收发大小究竟多少合适?

    udp不会粘包,udp协议本层对一次收发数据大小的限制是:

        65535 - ip包头(20) - udp包头(8) = 65507

    站在数据链路层,因为网卡的MTU一般被限制在了1500,所以对于数据链路层来说,一次收发数据的大小被限制在  1500 - ip包头(20) - udp包头(8) = 1472

    得到结论:

        如果sendto(num)

         num > 65507  报错

         1472 < num < 65507  在数据链路层拆包,udp本身就是不可靠协议,所以一旦拆包之后,造成的多个小数据包在网络传输中,如果丢任何一个,那么此次数据传输失败

         num < 1472 比较理想的状态

  • 相关阅读:
    第八章 Python 对象和类
    第七章 Python 盒子:模块、包和程序
    第六章 Python 函数(二)
    第五章 Python 函数(一)
    VS的32位、64位预处理定义;
    python 3D散点绘图;
    基于生长的棋盘格角点检测算法解读
    C++11: std::function<void()> func;
    有关pyinstaller打包程序后,转到其他电脑报“Failed to excute script"的问题;
    Qt: 监听文件夹QFileSystemWatcher;
  • 原文地址:https://www.cnblogs.com/chenyibai/p/9476272.html
Copyright © 2011-2022 走看看