TCP粘包拆包及NETTY解决方案

2023-11-06 02:40

本文主要是介绍TCP粘包拆包及NETTY解决方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

第一个netty应用

pom

<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.16.Final</version>
</dependency>

EchoServer

package com.aegis.netty.demo1;import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class EchoServer {public static void main(String[] args) throws Exception{//创建boss和worker线程(1)EventLoopGroup bossGroup = new NioEventLoopGroup(1);EventLoopGroup workerGroup = new NioEventLoopGroup();//服务端是ServerBootstrapServerBootstrap b = new ServerBootstrap();//初始化boss和work线程化两个线程(3)b.group(bossGroup, workerGroup)//声明NioServerSocketChannel(4).channel(NioServerSocketChannel.class)//初始化客户端Handler(5).childHandler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) {ch.pipeline().addLast(new EchoServerHandler());}});//绑定端口(6)ChannelFuture f = b.bind(8888).sync();f.channel().closeFuture().sync();}
}

EchoServerHandler

package com.aegis.netty.demo1;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;public class EchoServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {ByteBuf in = (ByteBuf) msg;System.out.println("Server received: " + in.toString(CharsetUtil.UTF_8));ctx.writeAndFlush(Unpooled.copiedBuffer("我是服务端".getBytes()));ctx.writeAndFlush(Unpooled.copiedBuffer("我是服务端".getBytes()));ctx.writeAndFlush(Unpooled.copiedBuffer("我是服务端".getBytes()));}
}

EchoClient

package com.aegis.netty.demo1;import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.net.InetSocketAddress;public class EchoClient {public static void main(String[] args) throws Exception{EventLoopGroup group = new NioEventLoopGroup();//客户端是BootstrapBootstrap b = new Bootstrap();b.group(group).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress("localhost", 8888)).handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch)throws Exception {ch.pipeline().addLast(new EchoClientHandler());}});ChannelFuture future = b.connect().sync();Channel channel = future.channel();channel.writeAndFlush(Unpooled.copiedBuffer(("客户端第1条消息" + channel.remoteAddress()).getBytes()));channel.writeAndFlush(Unpooled.copiedBuffer("客户端第2条消息".getBytes()));channel.writeAndFlush(Unpooled.copiedBuffer("客户端第3条消息".getBytes()));channel.closeFuture().sync();    }
}

EchoClientHandler

package com.aegis.netty.demo1;import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;public class EchoClientHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//System.out.println("客户端收到消息:[" + msg + "]");ByteBuf in = (ByteBuf) msg;System.out.println("Client received: " + in.toString(CharsetUtil.UTF_8));}
}

tcp拆包粘包

现象

上述代码客户端连上服务端后,发了三条消息分别是:客户端第1条localhost/127.0.0.1:8888 、客户端第2条消息、客户端第3条消息.服务端的逻辑是每次收到一条消息就打印出来,并给服务端回复三条消息我是服务端、我是服务端、我是服务端。所以服务端会收到3条消息,并给客户端返回9条消息,客户端能收到9条消息。但是由于TCP 粘包拆包原因,结果往往出乎意料

  1. 服务端收到3条消息,客户端收到6条消息

  1. 服务端收到3条消息,客户端收到一条消息

3、客户端服务端都收到一条消息

。。。。。各种可能

原因

抓包

wireshark

【最详细】Wireshark使用教程_未名编程的博客-CSDN博客_wireshark使用教程入门

代码中的消息改成了英文,方便展示,下面是抓到的包

0000 02 00 00 00 45 00 00 51 22 06 40 00 80 06 00 00 ....E..Q".@.....

0010 7f 00 00 01 7f 00 00 01 d0 06 22 b8 6b d7 1e ab ..........".k...

0020 bb 04 71 8f 50 18 08 05 95 64 00 00 63 6c 69 65 ..q.P....d..clie

0030 6e 74 20 31 20 6d 65 73 73 61 67 65 6c 6f 63 61 nt 1 messageloca

0040 6c 68 6f 73 74 2f 31 32 37 2e 30 2e 30 2e 31 3a lhost/127.0.0.1:

0050 38 38 38 38 24 8888$

0000 02 00 00 00 45 00 00 39 22 14 40 00 80 06 00 00 ....E..9".@.....

0010 7f 00 00 01 7f 00 00 01 d0 06 22 b8 6b d7 1e d4 ..........".k...

0020 bb 04 71 8f 50 18 08 05 1f 96 00 00 63 6c 69 65 ..q.P.......clie

0030 6e 74 20 32 20 6d 65 73 73 61 67 65 24 nt 2 message$

0000 02 00 00 00 45 00 00 39 22 18 40 00 80 06 00 00 ....E..9".@.....

0010 7f 00 00 01 7f 00 00 01 d0 06 22 b8 6b d7 1e e5 ..........".k...

0020 bb 04 71 8f 50 18 08 05 1f 84 00 00 63 6c 69 65 ..q.P.......clie

0030 6e 74 20 33 20 6d 65 73 73 61 67 65 24 nt 3 message$

下面是服务端收到的包,也就是channelRead中ByteBuf 16进制打印结果

 

63 6c 69 65 6e 74 20 31 20 6d 65 73 73 61 67 65 6c 6f 63 61 6c 68 6f 73 74 2f 31 32 37 2e 30 2e 30 2e 31 3a 38 38 38 38 24 63 6c 69 65 6e 74 20 32 20 6d 65 73 73 61 67 65 24 63 6c 69 65 6e 74 20 33 20 6d 65 73 73 61 67 65 24

netty解决tcp 粘包拆包方案

  1. 定长FixedLengthFrameDecoder

  2. 特殊字符切割DelimiterBasedFrameDecoder

下面以$符号作为切割符,分别在客户端和服务端添加DelimiterBasedFrameDecoder。发送的消息末尾加上$

ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
channel.writeAndFlush(Unpooled.copiedBuffer("客户端第2条消息$".getBytes()));

结果如下:服务端收到3条消息,客户端收到9条消息

        3.自定义协议实现文件的上传下载(后面介绍)

这篇关于TCP粘包拆包及NETTY解决方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/354008

相关文章

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

Python一次性将指定版本所有包上传PyPI镜像解决方案

《Python一次性将指定版本所有包上传PyPI镜像解决方案》本文主要介绍了一个安全、完整、可离线部署的解决方案,用于一次性准备指定Python版本的所有包,然后导出到内网环境,感兴趣的小伙伴可以跟随... 目录为什么需要这个方案完整解决方案1. 项目目录结构2. 创建智能下载脚本3. 创建包清单生成脚本4

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

C#文件复制异常:"未能找到文件"的解决方案与预防措施

《C#文件复制异常:未能找到文件的解决方案与预防措施》在C#开发中,文件操作是基础中的基础,但有时最基础的File.Copy()方法也会抛出令人困惑的异常,当targetFilePath设置为D:2... 目录一个看似简单的文件操作问题问题重现与错误分析错误代码示例错误信息根本原因分析全面解决方案1. 确保

C# LiteDB处理时间序列数据的高性能解决方案

《C#LiteDB处理时间序列数据的高性能解决方案》LiteDB作为.NET生态下的轻量级嵌入式NoSQL数据库,一直是时间序列处理的优选方案,本文将为大家大家简单介绍一下LiteDB处理时间序列数... 目录为什么选择LiteDB处理时间序列数据第一章:LiteDB时间序列数据模型设计1.1 核心设计原则

SpringBoot3匹配Mybatis3的错误与解决方案

《SpringBoot3匹配Mybatis3的错误与解决方案》文章指出SpringBoot3与MyBatis3兼容性问题,因未更新MyBatis-Plus依赖至SpringBoot3专用坐标,导致类冲... 目录SpringBoot3匹配MyBATis3的错误与解决mybatis在SpringBoot3如果

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

Python 字符串裁切与提取全面且实用的解决方案

《Python字符串裁切与提取全面且实用的解决方案》本文梳理了Python字符串处理方法,涵盖基础切片、split/partition分割、正则匹配及结构化数据解析(如BeautifulSoup、j... 目录python 字符串裁切与提取的完整指南 基础切片方法1. 使用切片操作符[start:end]2

Linux部署中的文件大小写问题的解决方案

《Linux部署中的文件大小写问题的解决方案》在本地开发环境(Windows/macOS)一切正常,但部署到Linux服务器后出现模块加载错误,核心原因是Linux文件系统严格区分大小写,所以本文给大... 目录问题背景解决方案配置要求问题背景在本地开发环境(Windows/MACOS)一切正常,但部署到

Java中InputStream重复使用问题的几种解决方案

《Java中InputStream重复使用问题的几种解决方案》在Java开发中,InputStream是用于读取字节流的类,在许多场景下,我们可能需要重复读取InputStream中的数据,这篇文章主... 目录前言1. 使用mark()和reset()方法(适用于支持标记的流)2. 将流内容缓存到字节数组