上个小节我们浅析了在Netty的使用的时候TCP的粘包和拆包的现象,Netty对此问题提供了相对比较丰富的解决方案

Netty提供了几个常用的解码器,帮助我们解决这些问题,其实上述的粘包和拆包的问题,归根结底的解决方案就是发送端给远程端一个标记,告诉远程端,每个信息的结束标志是什么,这样,远程端获取到数据后,根据跟发送端约束的标志,将接收的信息分切或者合并成我们需要的信息,这样我们就可以获取到正确的信息了

例如,我们刚才的例子中,我们可以在发送的信息中,加一个结束标志,例如两个远程端规定以行来切分数据,那么发送端,就需要在每个信息体的末尾加上行结束的标志,部分代码如下:

修改BaseClientHandler的req的构造:

  1. public BaseClientHandler() {
  2. //        req = ("BazingaLyncc is learner").getBytes();
  3. req = ("In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. His book w"
  4. + "ill give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the process "
  5. + "of configuring and connecting all of Netty’s components to bring your learned about threading models in ge"
  6. + "neral and Netty’s threading model in particular, whose performance and consistency advantages we discuss"
  7. + "ed in detail In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. Hi"
  8. + "s book will give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the"
  9. + " process of configuring and connecting all of Netty’s components to bring your learned about threading "
  10. + "models in general and Netty’s threading model in particular, whose performance and consistency advantag"
  11. + "es we discussed in detailIn this chapter you general, we recommend Java Concurrency in Practice by Bri"
  12. + "an Goetz. His book will give We’ve reached an exciting point—in the next chapter;the counter is: 1 2222"
  13. + "sdsa ddasd asdsadas dsadasdas" + System.getProperty("line.separator")).getBytes();
  14. }

我们在我们巨长的req中末尾加了System.getProperty("line.separator"),这样相当于给req打了一个标记

打完标记,其实我们这个示例中的server中还不知道是以行为结尾的,所以我们需要修改server的handler链,在inbound链中加一个额外的处理链,判断一下,获取的信息按照行来切分,我们很庆幸,这样枯燥的代码Netty已经帮我们完美地完成了,Netty提供了一个LineBasedFrameDecoder这个类,顾名思义,这个类名字中有decoder,说明是一个解码器,我们再看看它的详细声明:

  1. /**
  2. * A decoder that splits the received {@link ByteBuf}s on line endings.
  3. * <p>
  4. * Both {@code "\n"} and {@code "\r\n"} are handled.
  5. * For a more general delimiter-based decoder, see {@link DelimiterBasedFrameDecoder}.
  6. */
  7. public class LineBasedFrameDecoder extends ByteToMessageDecoder {
  8. /** Maximum length of a frame we're willing to decode.  */
  9. private final int maxLength;
  10. /** Whether or not to throw an exception as soon as we exceed maxLength. */
  11. private final boolean failFast;
  12. private final boolean stripDelimiter;
  13. /** True if we're discarding input because we're already over maxLength.  */
  14. private boolean discarding;
  15. private int discardedBytes;

它是继承ByteToMessageDecoder的,是将byte类型转化成Message的,所以我们应该将这个解码器放在inbound处理器链的第一个,所以我们修改一下Server端的启动代码:

  1. package com.lyncc.netty.stickpackage.myself;
  2. import io.netty.bootstrap.ServerBootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.channel.socket.nio.NioServerSocketChannel;
  10. import io.netty.handler.codec.LineBasedFrameDecoder;
  11. import io.netty.handler.codec.string.StringDecoder;
  12. import java.net.InetSocketAddress;
  13. public class BaseServer {
  14. private int port;
  15. public BaseServer(int port) {
  16. this.port = port;
  17. }
  18. public void start(){
  19. EventLoopGroup bossGroup = new NioEventLoopGroup(1);
  20. EventLoopGroup workerGroup = new NioEventLoopGroup();
  21. try {
  22. ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
  23. .childHandler(new ChannelInitializer<SocketChannel>() {
  24. protected void initChannel(SocketChannel ch) throws Exception {
  25. ch.pipeline().addLast(new LineBasedFrameDecoder(2048));
  26. ch.pipeline().addLast(new StringDecoder());
  27. ch.pipeline().addLast(new BaseServerHandler());
  28. };
  29. }).option(ChannelOption.SO_BACKLOG, 128)
  30. .childOption(ChannelOption.SO_KEEPALIVE, true);
  31. // 绑定端口,开始接收进来的连接
  32. ChannelFuture future = sbs.bind(port).sync();
  33. System.out.println("Server start listen at " + port );
  34. future.channel().closeFuture().sync();
  35. } catch (Exception e) {
  36. bossGroup.shutdownGracefully();
  37. workerGroup.shutdownGracefully();
  38. }
  39. }
  40. public static void main(String[] args) throws Exception {
  41. int port;
  42. if (args.length > 0) {
  43. port = Integer.parseInt(args[0]);
  44. } else {
  45. port = 8080;
  46. }
  47. new BaseServer(port).start();
  48. }
  49. }

这样,我们只是在initChannel方法中增加了一个LineBasedFrameDecoder这个类,其中2048是规定一行数据最大的字节数

我们再次运行,我们再看看效果:

可以看到客户端发送的两次msg,被服务器端成功地两次接收了,我们要的效果达到了

我们将LineBasedFrameDecoder中的2048参数,缩小一半,变成1024,我们再看看效果:

出现了异常,这个异常时TooLongFrameException,这个异常在Netty in Action中介绍过,帧的大小太大,在我们这个场景中,就是我们发送的一行信息大小是1076,大于了我们规定的1024所以报错了

我们再解决另一个粘包的问题,我们可以看到上节中介绍的那个粘包案例中,我们发送了100次的信息“BazingaLyncc is learner”,这个案例很特殊,这个信息是一个特长的数据,字节长度是23,所以我们可以使用Netty为我们提供的FixedLengthFrameDecoder这个解码器,看到这个名字就明白了大半,定长数据帧的解码器,所以我们修改一下代码:

BaseClientHandler:

  1. package com.lyncc.netty.stickpackage.myself;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.buffer.Unpooled;
  4. import io.netty.channel.ChannelHandlerContext;
  5. import io.netty.channel.ChannelInboundHandlerAdapter;
  6. public class BaseClientHandler extends ChannelInboundHandlerAdapter{
  7. private byte[] req;
  8. public BaseClientHandler() {
  9. req = ("BazingaLyncc is learner").getBytes();
  10. //        req = ("In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. His book w"
  11. //                + "ill give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the process "
  12. //                + "of configuring and connecting all of Netty’s components to bring your learned about threading models in ge"
  13. //                + "neral and Netty’s threading model in particular, whose performance and consistency advantages we discuss"
  14. //                + "ed in detail In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. Hi"
  15. //                + "s book will give We’ve reached an exciting point—in the next chapter we’ll discuss bootstrapping, the"
  16. //                + " process of configuring and connecting all of Netty’s components to bring your learned about threading "
  17. //                + "models in general and Netty’s threading model in particular, whose performance and consistency advantag"
  18. //                + "es we discussed in detailIn this chapter you general, we recommend Java Concurrency in Practice by Bri"
  19. //                + "an Goetz. His book will give We’ve reached an exciting point—in the next chapter;the counter is: 1 2222"
  20. //                + "sdsa ddasd asdsadas dsadasdas" + System.getProperty("line.separator")).getBytes();
  21. }
  22. @Override
  23. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  24. ByteBuf message = null;
  25. for (int i = 0; i < 100; i++) {
  26. message = Unpooled.buffer(req.length);
  27. message.writeBytes(req);
  28. ctx.writeAndFlush(message);
  29. }
  30. //        message = Unpooled.buffer(req.length);
  31. //        message.writeBytes(req);
  32. //        ctx.writeAndFlush(message);
  33. //        message = Unpooled.buffer(req.length);
  34. //        message.writeBytes(req);
  35. //        ctx.writeAndFlush(message);
  36. }
  37. @Override
  38. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
  39. ctx.close();
  40. }
  41. }

BaseServer:

  1. package com.lyncc.netty.stickpackage.myself;
  2. import io.netty.bootstrap.ServerBootstrap;
  3. import io.netty.channel.ChannelFuture;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.SocketChannel;
  9. import io.netty.channel.socket.nio.NioServerSocketChannel;
  10. import io.netty.handler.codec.FixedLengthFrameDecoder;
  11. import io.netty.handler.codec.string.StringDecoder;
  12. import java.net.InetSocketAddress;
  13. public class BaseServer {
  14. private int port;
  15. public BaseServer(int port) {
  16. this.port = port;
  17. }
  18. public void start(){
  19. EventLoopGroup bossGroup = new NioEventLoopGroup(1);
  20. EventLoopGroup workerGroup = new NioEventLoopGroup();
  21. try {
  22. ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
  23. .childHandler(new ChannelInitializer<SocketChannel>() {
  24. protected void initChannel(SocketChannel ch) throws Exception {
  25. ch.pipeline().addLast(new FixedLengthFrameDecoder(23));
  26. ch.pipeline().addLast(new StringDecoder());
  27. ch.pipeline().addLast(new BaseServerHandler());
  28. };
  29. }).option(ChannelOption.SO_BACKLOG, 128)
  30. .childOption(ChannelOption.SO_KEEPALIVE, true);
  31. // 绑定端口,开始接收进来的连接
  32. ChannelFuture future = sbs.bind(port).sync();
  33. System.out.println("Server start listen at " + port );
  34. future.channel().closeFuture().sync();
  35. } catch (Exception e) {
  36. bossGroup.shutdownGracefully();
  37. workerGroup.shutdownGracefully();
  38. }
  39. }
  40. public static void main(String[] args) throws Exception {
  41. int port;
  42. if (args.length > 0) {
  43. port = Integer.parseInt(args[0]);
  44. } else {
  45. port = 8080;
  46. }
  47. new BaseServer(port).start();
  48. }
  49. }

我们就是在channelhandler链中,加入了FixedLengthFrameDecoder,且参数是23,告诉Netty,获取的帧数据有23个字节就切分一次

运行结果:

可以看见,我们获取到了我们想要的效果

当然Netty还提供了一些其他的解码器,有他们自己的使用场景,例如有按照某个固定字符切分的DelimiterBasedFrameDecoder的解码器

我们再次修改代码:

BaseClientHandler.java

  1. package com.lyncc.netty.stickpackage.myself;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.buffer.Unpooled;
  4. import io.netty.channel.ChannelHandlerContext;
  5. import io.netty.channel.ChannelInboundHandlerAdapter;
  6. public class BaseClientHandler extends ChannelInboundHandlerAdapter{
  7. private byte[] req;
  8. public BaseClientHandler() {
  9. //        req = ("BazingaLyncc is learner").getBytes();
  10. req = ("In this chapter you general, we recommend Java Concurrency in Practice by Brian Goetz. $$__ His book w"
  11. + "ill give We’ve reached an exciting point—in the next chapter we’ll $$__ discuss bootstrapping, the process "
  12. + "of configuring and connecting all of Netty’s components to bring $$__ your learned about threading models in ge"
  13. + "neral and Netty’s threading model in particular, whose performance $$__ and consistency advantages we discuss"
  14. + "ed in detail In this chapter you general, we recommend Java  $$__Concurrency in Practice by Brian Goetz. Hi"
  15. + "s book will give We’ve reached an exciting point—in the next $$__ chapter we’ll discuss bootstrapping, the"
  16. + " process of configuring and connecting all of Netty’s components $$__ to bring your learned about threading "
  17. + "models in general and Netty’s threading model in particular, $$__ whose performance and consistency advantag"
  18. + "es we discussed in detailIn this chapter you general, $$__ we recommend Java Concurrency in Practice by Bri"
  19. + "an Goetz. His book will give We’ve reached an exciting $$__ point—in the next chapter;the counter is: 1 2222"
  20. + "sdsa ddasd asdsadas dsadasdas" + System.getProperty("line.separator")).getBytes();
  21. }
  22. @Override
  23. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  24. ByteBuf message = null;
  25. //        for (int i = 0; i < 100; i++) {
  26. //            message = Unpooled.buffer(req.length);
  27. //            message.writeBytes(req);
  28. //            ctx.writeAndFlush(message);
  29. //        }
  30. message = Unpooled.buffer(req.length);
  31. message.writeBytes(req);
  32. ctx.writeAndFlush(message);
  33. message = Unpooled.buffer(req.length);
  34. message.writeBytes(req);
  35. ctx.writeAndFlush(message);
  36. }
  37. @Override
  38. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
  39. ctx.close();
  40. }
  41. }

我们在req的字符串中增加了“$$__”这样的切割符,然后再Server中照例增加一个DelimiterBasedFrameDecoder,来切割字符串:

  1. ServerBootstrap sbs = new ServerBootstrap().group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port))
  2. .childHandler(new ChannelInitializer<SocketChannel>() {
  3. protected void initChannel(SocketChannel ch) throws Exception {
  4. ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,Unpooled.copiedBuffer("$$__".getBytes())));
  5. ch.pipeline().addLast(new StringDecoder());
  6. ch.pipeline().addLast(new BaseServerHandler());
  7. };
  8. }).option(ChannelOption.SO_BACKLOG, 128)
  9. .childOption(ChannelOption.SO_KEEPALIVE, true);

我们在initChannel中第一个inbound中增加了DelimiterBasedFrameDecoder,且规定切割符就是“$$__”,这样就能正常切割了,我们看看运行效果:

可以看到被分了20次读取,我们可以这样理解,客户端发送了2次req字节,每个req中有10个“$$__”,这样就是第11次切割的时候其实发送了粘包,第一个req中末尾部分和第二次的头部粘在了一起,作为第11部分的内容

而最后一部分的内容因为没有"$$__"切割,所以没有打印在控制台上~

其实这类的Handler还是相对比较简单的,真实的生产环境这些decoder只是作为比较基本的切分类,但是这些decoder还是很好用的~

希望讲的对您有所帮助~END~

TCP粘包拆包基本解决方案的更多相关文章

  1. TCP 粘包 - 拆包问题及解决方案

    目录 TCP粘包拆包问题 什么是粘包 - 拆包问题 为什么存在粘包 - 拆包问题 粘包 - 拆包 演示 粘包 - 拆包 解决方案 方式一: 固定缓冲区大小 方式二: 封装请求协议 方式三: 特殊字符结 ...

  2. Netty(三)TCP粘包拆包处理

    tcp是一个“流”的协议,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题. 粘包.拆包问题说明 假设客户端分别发送数据包D1和D ...

  3. TCP粘包/拆包问题

    无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河 ...

  4. TCP 粘包/拆包问题

    简介    TCP 是一个’流’协议,所谓流,就是没有界限的一串数据. 大家可以想想河里的流水,是连成一片的.期间并没有分界线, TCP 底层并不了解上层业务数据的具体含义 ,它会根据 TCP 缓冲区 ...

  5. Netty(二)——TCP粘包/拆包

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7814644.html 前面讲到:Netty(一)--Netty入门程序 主要内容: TCP粘包/拆包的基础知 ...

  6. Java网络编程基础之TCP粘包拆包

    TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想象河里的流水,他们是连成一片的,其间并没有分界线.TCP底层并不了解上层业务数据的具体含义,他会根据TCP缓冲区的实 ...

  7. Netty使用LineBasedFrameDecoder解决TCP粘包/拆包

    TCP粘包/拆包 TCP是个”流”协议,所谓流,就是没有界限的一串数据.TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被TC ...

  8. TCP粘包/拆包(Netty权威指南)

    无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个“流”协议,所谓流,就是没有界限的一串数据.大家可以想想河里的流水,是连成一片 ...

  9. 《精通并发与Netty》学习笔记(13 - 解决TCP粘包拆包(一)概念及实例演示)

    一.粘包/拆包概念 TCP是一个“流”协议,所谓流,就是没有界限的一长串二进制数据.TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认 ...

随机推荐

  1. vue实现简单评分效果

  2. [Python]定时任务框架 APScheduler

    1.使用APScheduler教程 参考博客地址

  3. 新手向——关于Python3.5在Windows 10 系统下发布模块的终极讲解

    博主自己在发布Python模块的时候也是摸索了好久啊,因为跟着书上写的步骤一步一步来终究会跪的节奏有木有啊!!!几经波折终于搞出来了,贴下来与诸君共勉.之前的步骤相信大家都已经知道了,那我们就直接跳过 ...

  4. 写一个Python的windows服务

    1. 安装pywin32和pyinstaller pip install pywin32 pip install pyinstaller 2.写一个服务Demo # -*- coding: utf-8 ...

  5. Java:浅克隆(shallow clone)与深克隆(deep clone)

    Summary 浅克隆与深克隆对于JavaSE来说,是个难度系数比较低的概念,但不应该轻视它. 假设一个场景:对于某个list,代码里并没有任何对其的直接操作,但里面的元素的属性却被改变了,这可能就涉 ...

  6. Page View Controllers

    Page View Controllers You use a page view controller to present content in a page-by-page manner. A ...

  7. 《DSP using MATLAB》示例Example 8.1

    终于看到第8章了,加油,继续努力! N为奇数,有极点位于实数轴上.事实上,所有极点位于Ωc=0.5为半径的圆上,而不是单位圆.

  8. python调用rpc实现分布式系统

    rpc 一般俗称,远程过程调用,把本地的函数,放到远端去调用. 通常我们调用一个方法,譬如: sumadd(10, 20),sumadd方法的具体实现要么是用户自己定义,要么存在于该语言的库函数中,也 ...

  9. WPF中Grid实现网格,表格样式通用类(转)

    /// <summary> /// 给Grid添加边框线 /// </summary> /// <param name="grid"></ ...

  10. Jitsi 开源视频会议远程桌面共享&&文档共享工具

    1. 特点 主要功能特点: 支持网络视频会议,使用SFU模式实现视频路由器功能. 支持SIP帐号注册电话呼叫. 支持安卓苹果终端. 支持文档共享功能,即时消息功能. 支持中文界面. 支持会议邀请,密码 ...