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

相关文章

javax.net.ssl.SSLHandshakeException:异常原因及解决方案

《javax.net.ssl.SSLHandshakeException:异常原因及解决方案》javax.net.ssl.SSLHandshakeException是一个SSL握手异常,通常在建立SS... 目录报错原因在程序中绕过服务器的安全验证注意点最后多说一句报错原因一般出现这种问题是因为目标服务器

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

MyBatis Plus 中 update_time 字段自动填充失效的原因分析及解决方案(最新整理)

《MyBatisPlus中update_time字段自动填充失效的原因分析及解决方案(最新整理)》在使用MyBatisPlus时,通常我们会在数据库表中设置create_time和update... 目录前言一、问题现象二、原因分析三、总结:常见原因与解决方法对照表四、推荐写法前言在使用 MyBATis

Java死锁问题解决方案及示例详解

《Java死锁问题解决方案及示例详解》死锁是指两个或多个线程因争夺资源而相互等待,导致所有线程都无法继续执行的一种状态,本文给大家详细介绍了Java死锁问题解决方案详解及实践样例,需要的朋友可以参考下... 目录1、简述死锁的四个必要条件:2、死锁示例代码3、如何检测死锁?3.1 使用 jstack3.2

html 滚动条滚动过快会留下边框线的解决方案

《html滚动条滚动过快会留下边框线的解决方案》:本文主要介绍了html滚动条滚动过快会留下边框线的解决方案,解决方法很简单,详细内容请阅读本文,希望能对你有所帮助... 滚动条滚动过快时,会留下边框线但其实大部分时候是这样的,没有多出边框线的滚动条滚动过快时留下边框线的问题通常与滚动条样式和滚动行

Oracle修改端口号之后无法启动的解决方案

《Oracle修改端口号之后无法启动的解决方案》Oracle数据库更改端口后出现监听器无法启动的问题确实较为常见,但并非必然发生,这一问题通常源于​​配置错误或环境冲突​​,而非端口修改本身,以下是系... 目录一、问题根源分析​​​二、保姆级解决方案​​​​步骤1:修正监听器配置文件 (listener.

MySQL版本问题导致项目无法启动问题的解决方案

《MySQL版本问题导致项目无法启动问题的解决方案》本文记录了一次因MySQL版本不一致导致项目启动失败的经历,详细解析了连接错误的原因,并提供了两种解决方案:调整连接字符串禁用SSL或统一MySQL... 目录本地项目启动报错报错原因:解决方案第一个:第二种:容器启动mysql的坑两种修改时区的方法:本地

安装centos8设置基础软件仓库时出错的解决方案

《安装centos8设置基础软件仓库时出错的解决方案》:本文主要介绍安装centos8设置基础软件仓库时出错的解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录安装Centos8设置基础软件仓库时出错版本 8版本 8.2.200android4版本 javas

Java空指针异常NullPointerException的原因与解决方案

《Java空指针异常NullPointerException的原因与解决方案》在Java开发中,NullPointerException(空指针异常)是最常见的运行时异常之一,通常发生在程序尝试访问或... 目录一、空指针异常产生的原因1. 变量未初始化2. 对象引用被显式置为null3. 方法返回null