本文主要是介绍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 粘包拆包原因,结果往往出乎意料
-
服务端收到3条消息,客户端收到6条消息
-
服务端收到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 粘包拆包方案
-
定长FixedLengthFrameDecoder
-
特殊字符切割DelimiterBasedFrameDecoder
下面以$符号作为切割符,分别在客户端和服务端添加DelimiterBasedFrameDecoder。发送的消息末尾加上$
ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));
channel.writeAndFlush(Unpooled.copiedBuffer("客户端第2条消息$".getBytes()));
结果如下:服务端收到3条消息,客户端收到9条消息
3.自定义协议实现文件的上传下载(后面介绍)
这篇关于TCP粘包拆包及NETTY解决方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!