首页 > 其他分享 >粘包

粘包

时间:2024-01-16 16:23:37浏览次数:32  
标签:len server json client msg 粘包 data

粘包

【一】什么是粘包

  • 须知:只有TCP有粘包现象,UDP永远不会粘包

【二】什么是粘包问题

  • 客户端发送需要执行的代码
  • 服务端接收到客户端传过来的代码
    • 服务端调用方法执行代码并拿到执行后的结果
    • 服务端将执行后的结果进行返回
  • 客户端接收到服务端返回的结果并做打印输出

【1】服务端

from socket import *
import subprocess

server = socket()
server.bind(('127.0.0.1', 8888))
server.listen(5)

while True:
    conn, client_addr = server.accept()
    while True:
        try:
            cmd_from_client = conn.recv(1024)
            if len(cmd_from_client) == 0:
                break
            msg_server = subprocess.Popen(cmd_from_client.decode('utf-8'),
                                          shell=True,
                                          stdout=subprocess.PIPE,
                                          stderr=subprocess.PIPE,
                                          )
            true_msg = msg_server.stdout.read()
            false_msg = msg_server.stderr.read()
            conn.send(true_msg)
            conn.send(false_msg)

        except Exception as e:
            break
    conn.close()

【2】客户端

from socket import *

while True:
    client = socket()
    client.connect(('127.0.0.1', 8888))
    msg = input('enter msg :>>>').strip()
    if len(msg) == 0: continue
    msg = msg.encode('utf-8')
    client.send(msg)
    msg_from_server = client.recv(1024)
    msg_from_server = msg_from_server.decode('gbk')
    print(msg_from_server)
    client.close()

【3】问题引入

  • 服务端:
    • 执行代码,代码为空会报错
    • 执行代码,返回的数据可能存在空/报错信息
  • 客户端:
    • 输入的指令长度,可能会超出范围
    • 接受到的服务端反馈的结果可能会特别多
    • 如何打印超出数据范围(缓存到系统里)的数据

【4】粘包问题

  • 在 TCP 协议中是流式协议,数据是源源不断的传入到客户端中,但是客户端可以接受到的信息的长度是有限的
  • 当接收到指定长度的信息后,客户端进行打印输出
    • 剩余的其他数据会被缓存到 内存中
  • 当再次执行其他命令时
    • 新的数据的反馈结果,会叠加到上一次没有完全打印完全的信息的后面,造成数据的错乱
  • 当客户端想打印新的命令的数据时,打印的其实是上一次没有打印完的数据
    • 对数据造成的错乱

【5】粘包问题解决思路

  • 拿到数据的总大小 recv_total_size
  • recv_size = 0 ,循环接收,每接收一次,recv_size += 接收的长度
  • 直到 recv_size = recv_total_size 表示接受信息完毕,结束循环

【三】UDP协议不存在粘包问题

  • 粘包问题出现的原因

    • TCP 协议是流式协议,数据像水流一样粘在一起,没有任何边界之分
    • 收数据没有接收干净,有残留,就会和下一次的结果混淆在一起
  • 解决粘包问题的核心法门就是

    • 每次都收干净
    • 不造成数据的混淆
  • 当我们启动udp服务端后,由udp客户端向服务端发送两条数据

  • 但是在udp服务端只接收到了一条数据

  • 这是因为 udp 是报式协议,传送数据过程中会将数据打包直接发走,不会对数据进行拼接操作(没有Nagle算法)

【四】TCP协议解决粘包问题

【1】解决思路

  • 利用 struct 模块将传输过去的数据的总长度 打包 + 到头部进行发送

【2】服务端

import json
from socket import *
import subprocess
import struct

server = socket()
server.bind(('127.0.0.1', 8888))
server.listen(5)

while True:
    conn, client_addr = server.accept()
    while True:
        try:
            cmd_from_client = conn.recv(1024)
            if len(cmd_from_client) == 0:
                break
            msg_server = subprocess.Popen(cmd_from_client.decode('utf-8'),
                                          shell=True,
                                          stdout=subprocess.PIPE,
                                          stderr=subprocess.PIPE,
                                          )
            true_msg = msg_server.stdout.read()
            false_msg = msg_server.stderr.read()
            total_size_len = len(true_msg) + len(false_msg)
            headers = {
                'total_size': total_size_len
            }
            json_data = json.dumps(headers)
            json_data_bytes = json_data.encode('utf-8')
            json_data_size_pack = struct.pack('i', len(json_data_bytes))
            conn.send(json_data_size_pack)
            conn.send(json_data_bytes)

            conn.send(true_msg)
            conn.send(false_msg)

        except Exception as e:
            break
    conn.close()

【3】客户端

from socket import *
import struct,json

while True:
    client = socket()
    client.connect(('127.0.0.1', 8888))
    msg = input('enter msg :>>>').strip()
    if len(msg) == 0: continue
    msg = msg.encode('utf-8')
    client.send(msg)
    json_data_size_unpack = client.recv(4)
    json_data_size = struct.unpack('i', json_data_size_unpack)[0]
    json_data_bytes = client.recv(json_data_size)
    json_dict=json_data_bytes.decode('utf8')
    headers_dict=json.loads(json_dict)
    total_len=headers_dict['total_size']

    recv_size=0
    while recv_size < total_len:
        msg_from_server = client.recv(1024)
        print(msg_from_server.decode('gbk'),end='')
        recv_size+=1024
    client.close()

【五】数据传输练习

【1】服务端

import json
import struct
from socket import *
import os

server = socket()
server.bind(('127.0.0.1', 9696))
server.listen(5)

while True:
    client, addr = server.accept()
    while True:

        file_name_data = client.recv(1024)
        file_name = file_name_data.decode('utf8')
        print(f"这是来自客户端想要下载的文件 :>>>> {file_name}")
        file_path = os.path.join(os.path.dirname(__file__), file_name)

        with open(file_path, 'rb') as f:
            video_data = f.read()

        message = {
            "data_length": len(video_data)
        }

        header_bytes = bytes(json.dumps(message), encoding='utf-8')

        header_len_bytes = struct.pack('i', len(header_bytes))

        # 发送消息头长度
        client.send(header_len_bytes)
        # 发送消息头
        client.send(header_bytes)
        # 发送视频数据
        client.sendall(video_data)

    conn.close()

【2】客户端

import os
from socket import *
import struct
import json

while True:
    client = socket()

    client.connect(('127.0.0.1', 9696))
    cmd_data = input("需要下载的文件名 :>>>> ").strip()
    new_file_name = input("请输入需要保存的文件名 :>>>> ").strip()
    file_path = os.path.join(os.path.dirname(__file__), new_file_name)
    cmd_data = cmd_data.encode('utf8')
    client.send(cmd_data)

    head_len_bytes = client.recv(4)
    head_len = struct.unpack('i', head_len_bytes)[0]

    head_bytes = client.recv(head_len)
    json_data = head_bytes.decode('utf-8')
    data = json.loads(json_data)

    video_size = data['data_length']
    video_data = b''
    while len(video_data) < video_size:
        video_data += client.recv(1024)

    with open(file_path, 'wb') as f:
        f.write(video_data)

标签:len,server,json,client,msg,粘包,data
From: https://www.cnblogs.com/unrealqcc/p/17967943

相关文章

  • 对解决粘包问题的理解
    【一】什么是粘包问题想象你正在通过一条很窄的管道(网络连接)发送一串珠子(数据包)。在管道的另一端,有人(接收方)正在接收这些珠子。为了让对方知道每串珠子的开始和结束,你决定在每串珠子之间放一小块纸条(数据包的边界标记)。但是,如果这些珠子和纸条在管道中相互挤压,可能会导致纸条丢......
  • TCP粘包/拆包,如何解决
    TCP粘包(TCPPacketStickiness):TCP粘包指的是发送方发送的多个小数据包被接收方一次性接收,形成一个大的数据包。这种情况可能会导致接收方难以正确解析消息的边界,因为多个消息被粘合在一起。TCP是面向流的协议,它不保留消息的边界信息,而是将数据流划分为小的数据块进行传输。TCP拆......
  • Netty源码学习6——netty编码解码器&粘包半包问题的解决
    系列文章目录和关于我零丶引入经过《Netty源码学习4——服务端是处理新连接的&netty的reactor模式和《Netty源码学习5——服务端是如何读取数据的》的学习,我们了解了服务端是如何处理新连接并读取客户端发送的数据的:netty的reactor:主reactor中的NioEventLoop监听accept事件,然......
  • 常见面试题-Netty线程模型以及TCP粘包拆包
    介绍一下Netty使用的线程模型?答:Netty主要基于主从Reactor多线程模型,其中主从Reactor多线程模型将Reactor分为两部分:mainReactor:监听ServerSocket,用来处理网络IO连接建立操作,将建立的SocketChannel指定注册给subReactorsubReactor:和建立起来的socket做数据交互和......
  • 04. 粘包问题
    一、什么是粘包  TCP是流式协议,数据向水流一样粘在一起,没有任何边界区分。如果接收数据没收干净,数据会有残留,就会和下一次的结果混淆在一起,就会出现粘包问题。粘包指的是发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾,出......
  • 10月23日粘包、struct模块以及json模块
    目录粘包如何解决粘包问题呢?struct模块json模块粘包粘包:tcp会把数据量较小,时间间隔较短的数据,当做同一个包发送粘包问题图粘包问题说白了就是客户端传给服务器的数据到服务器的时候有部分数据粘在了一块,而不是一条条的显示粘包产生情况大致图如何解决粘包问题呢?简单的方......
  • TCP 的重传机制;TCP 的粘包和拆包是什么?
    重传包括超时重传、快速重传、带选择确认的重传(SACK)、重复SACK四种。一、TCP重传机制1.1超时重传超时重传,是TCP协议保证数据可靠性的另一个重要机制,其原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成......
  • 10月23日粘包学习以及struct模块和json模块
    目录粘包如何解决粘包问题呢?struct模块json模块粘包粘包:tcp会把数据量较小,时间间隔较短的数据,当做同一个包发送粘包问题图粘包问题说白了就是客户端传给服务器的数据到服务器的时候有部分数据粘在了一块,而不是一条条的显示粘包产生情况大致图如何解决粘包问题呢?简单的方......
  • TCP 粘包
    TCP(TransmissionControlProtocol,传输控制协议)是一种传输层协议。TCP提供了以下主要功能:可靠性:TCP使用确认、重传和校验等机制来确保数据的可靠传输。它能够检测丢失、重复、损坏或乱序的数据,并采取相应的措施来保证数据的完整和正确性。有序性:TCP保证数据按照发送的顺序到达......
  • Netty编解码&粘包拆包&心跳机制&断线自动重连
    Netty编解码Netty涉及到编解码的组件有Channel、ChannelHandler、ChannelPipe等,先大概了解下这几个组件的作用。ChannelHandlerChannelHandler充当了处理入站和出站数据的应用程序逻辑容器。例如,实现ChannelInboundHandler接口(或ChannelInboundHandlerAdapter),你就可以接收入站事件......