粘包

一、什么是粘包

粘包是指发送方发送的若干数据到接收方,而接收方在接收数据时这些数据粘在一起,后一包数据头紧接着前一包数据尾部。

二、为什么会粘包

首先了解一下socket收发消息原理:
底层原理参考另一篇:socket收发消息底层原理
socket收发消息原理
在发数据时,一条数据的大小对应用程序是不可见的,即接收端从自己缓存区接收数据时,根本不知道自己要从缓存区接受多少数据。

那为什么只用TCP有粘包现象,而UDP没有?

  1. TCP 是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
  2. UDP 是没有连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。

TCP 协议的数据不会丢失,没有接收完的包,下次会继续接收,传输可靠,但是会造成粘包。

两种粘包情况:

  1. 发送端发送间隔小,数据量小,为了有效发送数据,使用Nagle算法,合成一个大的数据包。
  2. 发送端数据量大,但接受端接收数据量小。

三、粘包解决思路

  1. 在数据发送前,计算数据量大小,并将结果发送给接收端
#服务端
from socket import *
import subprocess
ip_port=('127.0.0.1',8080)
back_log=5
buffer_size=1024

tcp_server=socket(AF_INET,SOCK_STREAM)
tcp_server.bind(ip_port)
tcp_server.listen(back_log)

while True:
    conn,addr=tcp_server.accept()
    print('新的客户端链接',addr)
    while True:
        #收
        try:
            cmd=conn.recv(buffer_size)
            if not cmd:break
            print('收到客户端的命令',cmd)

            #执行命令,得到命令的运行结果cmd_res
            res=subprocess.Popen(cmd.decode('utf-8'),shell=True,
                                 stderr=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stdin=subprocess.PIPE)
            err=res.stderr.read()
            if err:
                cmd_res=err
            else:
                cmd_res=res.stdout.read()

            #发
            if not cmd_res:
                cmd_res='执行成功'.encode('gbk')

            length=len(cmd_res)
            conn.send(str(length).encode('utf-8'))
            client_ready=conn.recv(buffer_size)
            if client_ready == b'ready':
                conn.send(cmd_res)
        except Exception as e:
            print(e)
            break
#客户端
from socket import *
ip_port=('127.0.0.1',8080)
back_log=5
buffer_size=1024

tcp_client=socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
    cmd=input('>>: ').strip()
    if not cmd:continue
    if cmd == 'quit':break

    tcp_client.send(cmd.encode('utf-8'))


    #解决粘包
    length=tcp_client.recv(buffer_size)
    tcp_client.send(b'ready')

    length=int(length.decode('utf-8'))

    recv_size=0
    recv_msg=b''
    while recv_size < length:
        recv_msg += tcp_client.recv(buffer_size)
        recv_size=len(recv_msg) 


    print('命令的执行结果是 ',recv_msg.decode('gbk'))
tcp_client.close()
  1. 直接使用固定的四个字节来表示数据长度
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值