TCP粘包和拆包

news2024/10/6 20:32:24

TCP粘包和拆包

(1)TCP是面向连接的,面向流的,提供可靠性服务。收发两端(客户端和服务端)都要有一一成对的socket,因此,发送端为了将多个发给接收端的包,更有效的发给对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样做虽然提高了效率,但是接收端难于分辨出完整的数据包,因为面向流的通信是无消息保护边界的。
(2)由于TCP无消息保护边界,需要在接收端处理消息边界问题,也就是我们所说的粘包和拆包问题。
(3)TCP粘包、拆包图解
在这里插入图片描述
下面我们通过Netty实验还原场景:
我们通过这样的方法来还原粘包和拆包场景,客户端循环想服务端发送10条消息,服务端每次接收到消息即想客户端返回一个UUID,我们可以通过观察,这10条消息服务端接收过程中并不是一次性接收的,并且每次发送接收结果不一,说明部分消息发生了粘包现象(拆包现象)

服务端:
MyServer

package com.sgg.Netty.TCP;

import com.sgg.Netty.simple.NettyServerhandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class MyServer {
    public static void main(String[] args) throws InterruptedException {
        //创建BossGroup和WorkerGroup
        //说明
        //1、创建两个线程组BossGroup和WorkerGroup
        //2、BossGroup只负责处理请求,真正和客户端的业务处理会交给WorkerGroup
        //3、两个都是无限循环
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        //创建服务端的启动对象,配置启动参数
        ServerBootstrap bootstrap = new ServerBootstrap();

        //使用链式变成来进行设置
        bootstrap.group(bossGroup,workerGroup)       //设置两个线程组
                .channel(NioServerSocketChannel.class)     //使用NioServerSocketChannel作为服务器的通道实现
                .option(ChannelOption.SO_BACKLOG,128)       //设置线程队列的连接个数
                .childOption(ChannelOption.SO_KEEPALIVE,true)       //设置保持活动连接状态
                .childHandler(new MyServerInitializer());

        System.out.println(".....服务器准备好了");

        // Future-Listener机制

        //绑定一个端口并且同步,生成一个ChannelFuture对象
        ChannelFuture cf = bootstrap.bind(6668).sync();
        //给cf注册监听器,监控我们关心的事件
        cf.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                if(cf.isSuccess()){
                    System.out.println("监听端口成功");
                }else{
                    System.out.println("监听端口失败");
                }
            }
        });

        //对关闭通道进行监听
        cf.channel().closeFuture().sync();

    }
}

MyServerInitializer

package com.sgg.Netty.TCP;


import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;

public class MyServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new MyServerhandler());
    }
}

MyServerhandler

package com.sgg.Netty.TCP;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.nio.charset.Charset;
import java.util.UUID;

public class MyServerhandler extends SimpleChannelInboundHandler<ByteBuf> {
    private int count;

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
        //
        byte[] buffer = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(buffer);
        //将buffer转成字符串
        String message = new String(buffer, Charset.forName("utf-8"));

        System.out.println("服务器接收到数据"+ message);
        System.out.println("服务器端接收到消息="+ (++this.count));

        //服务端回送数据给客户端,回送 一个随机的ID
        ByteBuf responseByteBuf = Unpooled.copiedBuffer(UUID.randomUUID().toString()+"  ",Charset.forName("utf-8"));
        channelHandlerContext.writeAndFlush(responseByteBuf);

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

MyClient

package com.sgg.Netty.TCP;

import com.sgg.Netty.http.TestServerInitializer;
import com.sgg.Netty.simple.NettyClienthandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class MyClient {
    public static void main(String[] args) throws InterruptedException {
        //客户端需要一个事件循环组
        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        try {
            //创建客户端启动对象
            //注意客户端不是ServerBootstrap,是Bootstrap
            Bootstrap bootstrap = new Bootstrap();

            //设置相关参数
            bootstrap.group(eventLoopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new MyClientInitializer());
            System.out.println("客户端OK");
            //启动客户端
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();
            //给关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        } finally {
            //关闭
            eventLoopGroup.shutdownGracefully();
        }
    }
}

MyClientInitializer

package com.sgg.Netty.TCP;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;

public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new MyClienthandler());
    }
}

MyClienthandler

package com.sgg.Netty.TCP;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.nio.charset.Charset;

public class MyClienthandler extends SimpleChannelInboundHandler<ByteBuf> {


    private int count;

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
        byte[] buffer = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(buffer);

        String message = new String(buffer,Charset.forName("utf-8"));
        System.out.println("客户端接收到消息="+message);
        System.out.println("客户端接收消息数量="+(++this.count));
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //使用客户端发送10条数据
        for(int i=0;i<10;i++){
            ByteBuf buffer =  Unpooled.copiedBuffer("hello,server"+i, Charset.forName("utf-8"));
            ctx.writeAndFlush(buffer);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

最终我们可以看到:
服务端入下,同样客户端返回的随机数个数与接收次数统一
在这里插入图片描述那么我们怎么解决TCP的粘包和拆包呢??

TCP粘包和拆包解决方案

(1)使用自定义协议+编解码器 来解决
(2)关键就是要解决 服务器每次读取数据长度的问题,这个问题解决,就不会出现服务器多读或少读数据的问题,从而避免TCP粘包和拆包

具体实例:
(1)要求客户端发送5个Message对象,客户端每次发送一个Message对象
(2)服务端每次接收一个Message,分5次进行解码,每读到一个Message,会回复一个Message对象给客户端。

下面我们来讲解一下我们的思路:
(1)首先我们需要定义一个协议包的对象,也就是定义协议传输的格式
(2)我们需要根据这个协议包的格式来编写编码解码器的handler:MyMessageEncoder、MyMessageDecoder
(3)我们编写具体的消息发送和消息响应的自定义handler
在这里插入图片描述
下面我们来看具体实现:
MessageProtocal (协议包类)

package com.sgg.Netty.protocolTCP;

//协议包
public class MessageProtocal {
    private int len;// 关键
    private byte[] content;

    public int getLen() {
        return len;
    }

    public void setLen(int len) {
        this.len = len;
    }

    public byte[] getContent() {
        return content;
    }

    public void setContent(byte[] content) {
        this.content = content;
    }
}

MyMessageEncoder (编码器类:将消息转换成协议包对象)

package com.sgg.Netty.protocolTCP;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;


public class MyMessageEncoder extends MessageToByteEncoder<MessageProtocal> {
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, MessageProtocal messageProtocal, ByteBuf byteBuf) throws Exception {
        System.out.println("MyMessageEncoder encode 方法调用");
        byteBuf.writeInt(messageProtocal.getLen());     
        byteBuf.writeBytes(messageProtocal.getContent());

    }
}

MyMessageDecoder (解码器类:将协议包对象解码为消息)

package com.sgg.Netty.protocolTCP;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;

import java.util.List;

public class MyMessageDecoder extends ReplayingDecoder<Void> {
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        System.out.println("MyMessageDecoder decode方法被调用");
        //获取length、content
        int length= byteBuf.readInt();
        byte[] content = new byte[length];
        byteBuf.readBytes(content);

        //封装成MessageProtlcol对象,放入byteBuf,传递到下一个handler
        MessageProtocal messageProtocal = new MessageProtocal();
        messageProtocal.setLen(length);
        messageProtocal.setContent(content);

        //将封装的messageProtocal对象放入list
        list.add(messageProtocal);
    }
}

服务端类:MyServer、MyServerInitializer、MyServerhandler
MyServer

package com.sgg.Netty.protocolTCP;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class MyServer {
    public static void main(String[] args) throws InterruptedException {
        //创建BossGroup和WorkerGroup
        //说明
        //1、创建两个线程组BossGroup和WorkerGroup
        //2、BossGroup只负责处理请求,真正和客户端的业务处理会交给WorkerGroup
        //3、两个都是无限循环
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        //创建服务端的启动对象,配置启动参数
        ServerBootstrap bootstrap = new ServerBootstrap();

        //使用链式变成来进行设置
        bootstrap.group(bossGroup,workerGroup)       //设置两个线程组
                .channel(NioServerSocketChannel.class)     //使用NioServerSocketChannel作为服务器的通道实现
                .option(ChannelOption.SO_BACKLOG,128)       //设置线程队列的连接个数
                .childOption(ChannelOption.SO_KEEPALIVE,true)       //设置保持活动连接状态
                .childHandler(new MyServerInitializer());

        System.out.println(".....服务器准备好了");

        // Future-Listener机制

        //绑定一个端口并且同步,生成一个ChannelFuture对象
        ChannelFuture cf = bootstrap.bind(6668).sync();
        //给cf注册监听器,监控我们关心的事件
        cf.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                if(cf.isSuccess()){
                    System.out.println("监听端口成功");
                }else{
                    System.out.println("监听端口失败");
                }
            }
        });

        //对关闭通道进行监听
        cf.channel().closeFuture().sync();

    }
}

MyServerInitializer

package com.sgg.Netty.protocolTCP;


import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;

public class MyServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new MyMessageDecoder());  //添加加码器handler到pipeline
        pipeline.addLast(new MyMessageEncoder());  //添加解码器handler到pipeline
        pipeline.addLast(new MyServerhandler());
    }
}

MyServerhandler

package com.sgg.Netty.protocolTCP;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.nio.charset.Charset;
import java.util.UUID;

public class MyServerhandler extends SimpleChannelInboundHandler<MessageProtocal> {


    private int count;

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageProtocal messageProtocal) throws Exception {
        //接收到数据,并处理
        int len = messageProtocal.getLen();
        byte[] content = messageProtocal.getContent();

        System.out.println();
        System.out.println();
        System.out.println("服务器收到消息如下:");
        System.out.println("长度="+len);
        System.out.println("内容="+new String(content,Charset.forName("utf-8")));
        
        System.out.println("服务器接收到消息包数量:"+(++this.count));

        //回复消息
        //定义一个字符串
        String responseContent = UUID.randomUUID().toString();

        //将字符串转为byte数组
        byte[] rescontent = responseContent.getBytes("utf-8");
        //得到数据长度
        int reslength = responseContent.getBytes("utf-8").length;
        //封装成messageProtocal1对象
        MessageProtocal messageProtocal1 = new MessageProtocal();
        messageProtocal1.setLen(reslength);
        messageProtocal1.setContent(rescontent);

        channelHandlerContext.writeAndFlush(messageProtocal1);

    }
}

客户端类:MyClient、MyClientInitializer、MyClienthandler
MyClient

package com.sgg.Netty.protocolTCP;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

public class MyClient {
    public static void main(String[] args) throws InterruptedException {
        //客户端需要一个事件循环组
        NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        try {
            //创建客户端启动对象
            //注意客户端不是ServerBootstrap,是Bootstrap
            Bootstrap bootstrap = new Bootstrap();

            //设置相关参数
            bootstrap.group(eventLoopGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new MyClientInitializer());
            System.out.println("客户端OK");
            //启动客户端
            ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6668).sync();
            //给关闭通道进行监听
            channelFuture.channel().closeFuture().sync();
        } finally {
            //关闭
            eventLoopGroup.shutdownGracefully();
        }
    }
}

MyClientInitializer

package com.sgg.Netty.protocolTCP;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;

public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();

        pipeline.addLast(new MyMessageEncoder());
        pipeline.addLast(new MyMessageDecoder());
        pipeline.addLast(new MyClienthandler());
    }
}

MyClienthandler

package com.sgg.Netty.protocolTCP;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class MyClienthandler extends SimpleChannelInboundHandler<MessageProtocal> {


    private int count;

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, MessageProtocal messageProtocal) throws Exception {
        int len = messageProtocal.getLen();
        byte[] content = messageProtocal.getContent();

        System.out.println();
        System.out.println();
        System.out.println("客户端接收消息如下:");
        System.out.println("长度:"+len);
        System.out.println("内容:"+new String(content,Charset.forName("utf-8")));

        System.out.println("客户端接收消息数量"+(++this.count));
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //使用客户端发送10条数据
        for(int i=0;i<5;i++){
            String mes = "今天天气冷";
            byte[] content = mes.getBytes(Charset.forName("utf-8"));
            int length = mes.getBytes(Charset.forName("utf-8")).length;

            //创建协议包对象
            MessageProtocal messageProtocal = new MessageProtocal();
            messageProtocal.setLen(length);
            messageProtocal.setContent(content);

            ctx.writeAndFlush(messageProtocal);

        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("异常信息="+cause.getMessage());
        ctx.close();
    }


}

如上我们可以得到结果如下,不论怎么发送,消息永远不会发生粘包和拆包的现象:
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/11629.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【前端】Vue+Element UI案例:通用后台管理系统-代码总结

文章目录前言项目文件目录apimockServehome.jspermission.jsindex.jsmock.jsuser.jsassertcomponentsCommonAside.vueCommonHeader.vueCommonTags.vuedataechartsDataorder.jsuser.jsvideo.jsmockDatatableData.jsuserData.jsvideoData.jsCountData.jsMenuData.jsTableData.jsT…

389. 找不同(简单不一定知道)

问题描述&#xff1a; 给定两个字符串 s 和 t &#xff0c;它们只包含小写字母。 字符串 t 由字符串 s 随机重排&#xff0c;然后在随机位置添加一个字母。 请找出在 t 中被添加的字母。 示例 &#xff1a; 示例 1&#xff1a; 输入&#xff1a;s "abcd", t …

大学生抗疫逆行者网页作业 感动人物HTML网页代码成品 最美逆行者dreamweaver网页模板 致敬疫情感动人物网页设计制作

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

多维时序 | MATLAB实现ELM极限学习机多维时序预测(股票价格预测)

多维时序 | MATLAB实现ELM极限学习机多维时序预测(股票价格预测) 目录 多维时序 | MATLAB实现ELM极限学习机多维时序预测(股票价格预测)效果一览基本介绍程序设计结果输出参考资料效果一览 基本介绍

MySQL-僵持锁

前言 一个僵持锁&#xff08;deadlocks&#xff09;是指锁处于僵持的状态&#xff0c;持有锁的事务既得不到期望的资源&#xff0c;也不愿意释放其他事务需要的资源&#xff0c;也就是&#xff0c;多个锁相互之间都持有其他锁所需的资源&#xff0c;所有的事务都在等待各自需要…

防止重复下单(redis+数据库唯一索引requestId实现幂等)

文章目录为什么会重复下单如何防止重复下单利用数据库实现幂等利用Redis防重为什么会重复下单 为什么会重复下单&#xff0c;对于订单服务而言&#xff0c;就是接到了多个下单的请求&#xff0c;原因可能有很多&#xff0c;最常见的是这两种&#xff1a; 用户重复提交网络原因…

使用easygui制作app

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 使用easygui制作app [太阳]选择题 对于以下python代码表述错误的一项是? import easygui easygui.msgbox("我是msgbox","msgbox标题") choices["A",…

学生网页设计作业源码(HTML+CSS)——海贼王6页代码质量好

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

【Pytorch with fastai】第 13 章 :卷积神经网络

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

数据结构之线性表的顺序存储结构

配套环境 clion + g++ 顺序存储的定义 线性表的顺序存储结构,指的是用一段地址连续的存储单元一次存储线性表中的数据元素 设计思路 使用一维数组来实现顺序存储结构 存储空间:T* m_array 当前长度:int m_length 顺序存储结构的元素获取操作 判断目标位置是否合法 如…

Backblaze 2022 Q3 硬盘故障质量报告解读

在9月份&#xff0c;我们更新了Backblaze 2022上半年的中期质量报告解读&#xff08;Backblaze2022中期SSD故障质量报告解读&#xff09;&#xff0c;基于报告中的分析数据&#xff0c;Backblaze也向外界传递作证了一个信息&#xff1a;固态硬盘SSD的长期可靠性比机械硬盘HDD要…

听说某宝抢购脚本大家都会了?那就在来个某东茅台抢购脚本吧。

前言 某宝脚本一搜能搜一大堆&#xff0c;就是不知道具体有没有用&#xff0c;但是这款某东的代码于11-17还是可用的&#xff0c;大家拿去白嫖吧&#xff01; 需要用到的一些工具 Python版本&#xff1a;3.7.8相关模块&#xff1a;DecryptLogin模块&#xff1b;argparse模块&am…

webpack5 Preload / Prefetch

代码分离 | webpack 中文文档webpack 是一个模块打包器。它的主要目标是将 JavaScript 文件打包在一起&#xff0c;打包后的文件用于在浏览器中使用&#xff0c;但它也能够胜任转换&#xff08;transform&#xff09;、打包&#xff08;bundle&#xff09;或包裹&#xff08;pa…

【云原生 | 44】Docker搭建Registry私有仓库之管理访问权限

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 &#x1f3c5;阿里云ACE认证高级工程师 &#x1f3c5;阿里云开发者社区专…

C++ Reference: Standard C++ Library reference: Containers: deque: deque: rend

C官网参考链接&#xff1a;https://cplusplus.com/reference/deque/deque/rend/ 公有成员函数 <deque> std::deque::rend C98 reverse_iterator rend(); const_reverse_iterator rend() const; C11 reverse_iterator rend() noexcept; const_reverse_iterator rend() c…

【Vue】VueX 的语法详解(3)

在VueX&#xff08;叉&#xff09;里面有一个潜移默化&#xff0c;或者说本质上的一个约束是什么&#xff1f; mutation里面只允许‍‍写同步代码&#xff0c;不允许写‍‍异步代码&#xff0c;虽然它不强制的限制你说你写了就给你报错&#xff0c;但实际上它的设计里面要求‍‍…

2022跨境出海:中东地区网红营销现状及特点

提到中东&#xff0c;大家的第一印象可能就是“头顶一块布&#xff0c;天下我最富”的土豪形象&#xff0c;近期的卡塔尔世界杯也再次给外界展示了他们的壕无人性。但由于宗教影响&#xff0c;他们的很多线下交际活动受到限制&#xff0c;也使得他们在互联网世界十分活跃&#…

申报须知,2022年滁州市各区县高新技术企业奖励政策变化,明光市

为了帮助企业提前准备和更好地开展2023年高新技术企业认定申报工作&#xff0c;安徽省大力鼓励企业申报高新技术企业&#xff0c;相应出台了相关政策&#xff0c;同时对于高企申报也有很多奖补&#xff0c;下面小编汇总了滁州市琅琊区、南谯区、天长市、明光市、来安县、全椒县…

jq扩展机制

1、在$挂上自定义函数方法&#xff1a; 如果想自定义函数方法&#xff0c;而且能通过$调用&#xff0c;那就需要用到extend&#xff08;&#xff09;方法&#xff1b;格式&#xff1a;$.extend({}) ; <script>$.extend({yiyi:function(){console.log("yiyi")}…

唯亚威VIAVI FI-60光纤识别器

光纤识别器可以轻松地识别光信号&#xff0c;而无需断开光缆或中断网络交通。也可以转换为光功率计&#xff08;OPM&#xff09;。 VIAVI FI-60 LFI能够使用户轻松检测光信号&#xff0c;而无需断开光缆或中断网络交通。FI-60 LFI还包括了独特的VIAVI SafeChekTM &#xff0c;…