python 粘包

一. 粘包

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

  TCP粘包成因 :

    1. TCP协议的拆包机制 :

      当发送端缓冲区的长度大于网卡的MTU时,TCP会将这次发送的数据拆成几个数据报发送出去.

      MTU是Maximum Transmission Unit的缩写.意思就是网络上传输的最大数据包.MTU的单位是字节.大部分网络设备的MTU都是1500. 如果本机的MTU比网关的的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据被碎片,增加丢包率,降低网络速度.

    2. 面向字节流通信特点和Nagle算法

      TCP(transport control protocol,传输控制协议) 是面向连接,面向字节流的,提供高可靠性服务.收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块.然后进行封包.

      这样,接收端就需要有对应的拆包机制,才可以分辨出来数据的格式等,即面向字节流的通信是没有消息保护边界的(即不会智能断句).

      对于空消息: TCP是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务器端都添加空消息的处理机制,防止程序卡住,而UDP是基于数据报的,即使是你输入的是空内容,也可以被发送,UDP协议会帮你封装上消息头发过去.

      可靠但会粘包的TCP协议,TCP协议的数据不会丢失,一次没有接受完,下次会继续接收,接收端总是在收到ack后才会清除缓冲区内容.数据是可靠的,但是会粘包.

    3. 补充

      使用TCP协议发送时,由于TCP是数据流协议,因此不存在包大小的限制(暂不考虑缓存区的大小),这是指用send函数时,数据长度参数不受限制. 而实际上,所指定的这段数据并不一定会一次性发送出去,如果这段数据比较长,会被分段发送.如果比较短,可能会等待和下一次数据一起发送.

  TCP会发生粘包的两种情况 :

    1. 发送方的缓存机制 : 发送端需要等缓冲区满才会进行发送,造成粘包(发送数据时间间隔很短,数据量很小,会合到一起,产生粘包)

    2. 接收方的缓存机制 : 接收方不及时接受缓存区的包,造成多个数据包(客户端发送了一段数据,服务端只接受了一小部分,服务端下次再接收时会继续接收上次遗留的数据,产生粘包)

#粘包现象
# 服务器
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不发生粘包原因:

    1. UDP(user datagram protocol,用户数据报协议) 是无连接的,面向数据报的,提供高效率服务.不会使用数据块的合并优化算法,由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP数据包,在每个UDP数据包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了. 即面向消息(数据报)的通信是有消息保护边界的.

    2. 对于空消息 : TCP是基于数据流的,于是收发的内容不能为空,这就需要在客户端和服务器端都添加空消息的处理机制,防止程序卡住,而UDP是基于数据报的,即使输入的内容是空内容(直接回车),也可以被发送,UDP协议会帮你封装上消息头发送出去.

    3. 不可靠不粘包的UDP协议 : UDP的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成,若是y;x数据就丢失,这以为这UDP根本不会粘包,但是会丢失数据,不可靠.

    4. 使用UDP协议发送数据时,用sendto函数最大可以发送数据的长度为 : 65535 - IP头(20) - UDP报头(8) = 65507字节. 用sendto函数发送数据时,如果发送数据长度大于该值, 则函数会返回错误(丢弃这个包,不进行发送).

原文地址:https://www.cnblogs.com/dong-/p/9475629.html