粘包问题的解决策略

     由于底层的 TCP 无法理解上层业务数据,所以在底层是无法保证数据包不被拆分和重组的 , 这个问题只能通过上层的应用协议栈设计来解决,根据业界主流的协议的解决方案, 可以归纳如下:
  1. 消息定长, 例如每个报文的大小固定长度200字节,如果不够,空位补齐空格;
  2. 在包尾部添加回车换行符进行分割, 例如 FTP 协议;
  3. 将消息分为消息头和消息体,消息头中包含表示消息总长度(或者消息具体长度)的字段,通常设计思路为消息头的第一个字段使用 int32 来表示消息的总长度;
  4. 更复杂的应用协议层;
 

一. LineBasedFrameDecoder 与 StringDecoder

LineBasedFrameDecoder 与 StringDecoder 的工作原理

  LineBasedFrameDecoder 的工作原理是它依次遍历 ByteBuf 中得可读字节,判断看是否有 '\n' 或者  '\r\n' ,  如果有,就以此位置为结束位置,从可读索引到结束位置区间的字节组成一行.他是以换行符为结束标志的解码器.支持携带结束符或者不携带结束符两种编码方式,同时支持配置单行最大长度 . 如果连续读取到最大长度后仍然没有发现换行符,则抛出异常,同时忽略掉之前读到的异常码流.
 
StringDecoder 的功能非常简单,就是将收到的对象转换成字符串,然后继续调用后面的handler 
  LineBasedFrameDecoder + StringDecoder  组合就是按换行切换的文本解码器, 它被设计来用于支持 TCP 的粘包和拆包.
 
使用如下:
package time.server.impl;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
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.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder; /**
* TODO
*
* @description
* @author mjorcen
* @time 2015年5月25日 下午2:50:57
*/
public class NTimeServerImpl { public void bind(int port) {
// 创建两个NioEventLoopGroup 实例,NioEventLoopGroup
// 是一个线程组,它包含一组NIO线程,专门用于处理网络事件的处理,实际上他们就是Reactor 线程组
// 这里创建两个的原因是一个用于服务端接收用户的链接,另一个用于进行SocketChannel的网络读写
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup(); try {
// 创建一个 ServerBootstrap ,它是netty用于NIO服务端的辅助启动类,目的是降低服务端的开发复杂度.
ServerBootstrap bootstrap = new ServerBootstrap();
// 设定 服务端接收用户请求的线程组和用于进行SocketChannel网络读写的线程组
bootstrap.group(bossGroup, workerGroup);
// 设置创建的 channel 类型
bootstrap.channel(NioServerSocketChannel.class);
// 配置 NioServerSocketChannel 的 tcp 参数, BACKLOG 的大小
bootstrap.option(ChannelOption.SO_BACKLOG, 1024);
// 绑定io处理类(childChannelHandler).他的作用类似于 reactor 模式中的 handler
// 类,主要用于处理网络 I/O 事件,例如对记录日志,对消息进行解码等.
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(1024)); // 加入行处理器
ch.pipeline().addLast(new StringDecoder()); // 加入字符串解码器
ch.pipeline().addLast(new TimeServerHandler());
}
});
// 绑定端口,随后调用它的同步阻塞方法 sync 等等绑定操作成功,完成之后 Netty 会返回一个 ChannelFuture
// 它的功能类似于的 Future,主要用于异步操作的通知回调.
ChannelFuture channelFuture = bootstrap.bind(port).sync();
// 等待服务端监听端口关闭,调用 sync 方法进行阻塞,等待服务端链路关闭之后 main 函数才退出.
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 优雅的退出,释放线程池资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
} public static void main(String[] args) {
NTimeServerImpl server = new NTimeServerImpl();
server.bind(9091);
} }

ServerHandler

package time.server.impl;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; import java.util.Date; import time.TimeConfig; /**
* TODO
*
* @description
* @author ez
* @time 2015年5月25日 下午3:06:09
*/
public class TimeServerHandler extends ChannelHandlerAdapter implements
TimeConfig { /*
* (non-Javadoc)
*
* @see io.netty.channel.ChannelHandlerAdapter#channelRead(io.netty.channel.
* ChannelHandlerContext, java.lang.Object)
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
String body = (String) msg;
System.out.println("The time server receive order : " + body);
String currentTime = QUERY.equalsIgnoreCase(body) ? new Date()
.toString() : "BAD ORDER";
currentTime += System.getProperty("line.separator");
System.out.println("currentTime : " + currentTime);
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes("utf-8"));
ctx.writeAndFlush(resp);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
// 当出现异常时,释放资源.
ctx.close();
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} }

Client

package time.client.impl;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder; /**
* TODO
*
* @description
* @author ez
* @time 2015年5月25日 下午3:17:29
*/
public class NTimeClient { public void connect(int port, String host) throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
try { Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group);
bootstrap.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.TCP_NODELAY, true);
bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new LineBasedFrameDecoder(1024));
ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(new TimeClientHandler());
}
}); // 发起异步链接操作
ChannelFuture future = bootstrap.connect(host, port).sync(); // 等待客户端链路关闭
future.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
} public static void main(String[] args) throws Exception { NTimeClient client = new NTimeClient();
client.connect(9091, "localhost");
}
}

ClientHandler

package time.server.impl;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; import java.util.Date; import time.TimeConfig; /**
* TODO
*
* @description
* @author ez
* @time 2015年5月25日 下午3:06:09
*/
public class TimeServerHandler extends ChannelHandlerAdapter implements
TimeConfig { /*
* (non-Javadoc)
*
* @see io.netty.channel.ChannelHandlerAdapter#channelRead(io.netty.channel.
* ChannelHandlerContext, java.lang.Object)
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg)
throws Exception {
String body = (String) msg;
System.out.println("The time server receive order : " + body);
String currentTime = QUERY.equalsIgnoreCase(body) ? new Date()
.toString() : "BAD ORDER";
currentTime += System.getProperty("line.separator");
System.out.println("currentTime : " + currentTime);
ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes("utf-8"));
ctx.writeAndFlush(resp);
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
// 当出现异常时,释放资源.
ctx.close();
} @Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
} }

2: DelimiterBasedFrameDecoder
   

  

DelimiterBasedFrameDecoder 跟 LineBasedFrameDecoder 很相似 , 只是增加以自定义的分割符.

  

                    ByteBuf buf = Unpooled.copiedBuffer("$".getBytes("utf-8"));
ch.pipeline().addLast(
new DelimiterBasedFrameDecoder(1024, buf));
3: FixedLengthFrameDecoder 定长的分割器. 
 
                    ch.pipeline().addLast(new FixedLengthFrameDecoder(1024));
以上内容出自 : <Netty  权威指南> 

netty 粘包的解决策略的更多相关文章

  1. netty 粘包问题处理

    netty 粘包问题处理 key words: netty 粘包 解包 半包 TCP 一般TCP粘包/拆包解决办法 定长消息,例如每个报文长度固定,不够补空格 使用回车换行符分割,在包尾加上分割符,例 ...

  2. Netty 粘包/半包原理与拆包实战

    Java NIO 粘包 拆包 (实战) - 史上最全解读 - 疯狂创客圈 - 博客园 https://www.cnblogs.com/crazymakercircle/p/9941658.html 本 ...

  3. Netty 粘包 拆包 | 史上最全解读

    Netty 粘包/半包原理与拆包实战(史上最全) 疯狂创客圈 Java 聊天程序[ 亿级流量]实战系列之13 [博客园 总入口 ] 本文的源码工程:Netty 粘包/半包原理与拆包实战 源码 本实例是 ...

  4. tcp粘包、解决粘包问题

    目录 subproess模块 TCP粘包问题 粘包两种情况 解决粘包问题 struct模块的使用 使用struct模块解决粘包 优化解决粘包问题 上传大文件 服务端 客户端 UDP协议 upd套接字 ...

  5. netty解决TCP的拆包和粘包的解决办法

    TCP粘包.拆包问题 熟悉tcp编程的可能知道,无论是服务端还是客户端,当我们读取或者发送数据的时候,都需要考虑TCP底层的粘包个拆包机制. tcp是一个“流”协议,所谓流就是没有界限的传输数据,在业 ...

  6. Netty 粘包/拆包应用案例及解决方案分析

    熟悉TCP变成的可以知道,无论是客户端还是服务端,但我们读取或者发送消息的时候,都需要考虑TCP底层粘包/拆包机制,下面我们先看一下TCP 粘包/拆包和基础知识,然后模拟一个没有考虑TCP粘包/拆包导 ...

  7. Netty 粘包 & 拆包 & 编码 & 解码 & 序列化 介绍

    目录: 粘包 & 拆包及解决方案 ByteToMessageDecoder 基于长度编解码器 基于分割符的编解码器 google 的 Protobuf 序列化介绍 其他的 前言 Netty 作 ...

  8. Netty - 粘包和半包(上)

    在网络传输中,粘包和半包应该是最常出现的问题,作为 Java 中最常使用的 NIO 网络框架 Netty,它又是如何解决的呢?今天就让我们来看看. 定义 TCP 传输中,客户端发送数据,实际是把数据写 ...

  9. Netty粘包问题(六)

    netty使用tcp/ip协议传输数据,而tcp/ip协议是类似水流一样的数据传输方法.多次访问的时候可能出现粘包的问题,解决这种问题的方式有如下几种. 一.定长数据流 二.特殊结束符 三.

随机推荐

  1. Hibernate关联关系之多对多

    多对多映射关系 建立多对多单向关联关系 创建Employee类: public class Employee { private Integer empid; //员工编号 private Strin ...

  2. Django session 源码流程

    流程 Django session源码流程 首先执行的是SessionMiddleware的init方法 import_module(settings.SESSION_ENGINE) 导入了一个 dj ...

  3. 卡在checking installable status

    npm install卡在checking installable status 笔者在使用NPM过程中经常会用到npm install命令,发现有时候会卡在checking installable ...

  4. JAVA自学笔记21

    JAVA自学笔记21 1.转换流 由于字节流操作中文不是非常方便,因此java提供了转换流 字符流=字节流+编码表 1)编码表 由字符及其对应的数值组成的一张表 图解: 2)String类的编码和解码 ...

  5. REST风格的增删改查(1)

    一.RESTFUL风格的CRUD(增删改查)案例 1.需求: ①显示所有员工信息:URI:emps,请求方式:GET ②添加员工所有信息:显示添加页面:URI:emp,请求方式:GET, 添加员工信息 ...

  6. FIlter(2)—案例

    案例 demo2,login.jsp请求提交到hello.jsp,该页面中有两个text,分别使用两个Filter链拦截,验证账号密码是否正确,把账号密码设置到Filter初始化参数中 login.j ...

  7. golang gob 有什么优势? gob/protobuf/json/xml 效率对比,benchmark 压力测试

    TODO 待研究: https://blog.csdn.net/love_se/article/details/7941876 https://blog.csdn.net/wangshubo1989/ ...

  8. VUE温习:nextTick、$refs、嵌套路由、keep-alive缓存、is特性、路由属性用法、路由钩子函数

    一.$nextTick 1.vue的dom执行异步更新,只要观察到数据变化,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变. 2.vue.$nextTick(cb),数据发生变化,更新 ...

  9. jQuery之end()和pushStack()

    原文: https://www.cnblogs.com/AndyWithPassion/archive/2012/02/06/jquery_pushStack_javascript.html jque ...

  10. SSE图像算法优化系列二十九:基础的拉普拉斯金字塔融合用于改善图像增强中易出现的过增强问题(一)

    拉普拉斯金字塔融合是多图融合相关算法里最简单和最容易实现的一种,我们在看网络上大部分的文章都是在拿那个苹果和橙子融合在一起,变成一个果橙的效果作为例子说明.在这方面确实融合的比较好.但是本文我们主要讲 ...