本节大纲:

1、Handler的执行顺序
2、自定义二进制协议(每条完整数据的组成),从而解决拆包和粘包。
3、通过为每个channel创建新的handler,从而解决即使handler中使用全局变量,也可以避免竞态条件。

1、Handler的执行顺序。

client中pipeline顺序:
//first,add codec
pipeline.addLast(new BigIntegerDecoder());
pipeline.addLast(new NumberEncoder());
//then,add business logic
pipeline.addLast(new FactorialClientHandler());

server中pipeline顺序:
//first,codec
pipeline.addLast(new BigIntegerDecoder());
pipeline.addLast(new NumberEncoder());
//then,business logic
pipeline.addLast(new FactorialServerHandler());

总结:
写(outbound):自下而上,跳过inbound
读(inbound): 自上而下,跳过outbound
Codec放在上边,业务逻辑handler放在下边。

2、自定义二进制协议(每条完整数据的组成),从而解决拆包和粘包。

每条完整数据的组成:'F'+4个字节的长度+数据

将传进来的number编码为二进制,在其前边加上'F'和4个字节的长度,作为前缀。
例如:42被编码为:'F',0,0,0,1,42 

客户端:

public class FactorialClientHandler extends SimpleChannelInboundHandler<BigInteger> {

    @Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < 1024; j++) {
sb.append(j);
}
BigInteger bigInt = new BigInteger(sb.toString()); ChannelFuture future = ctx.writeAndFlush(bigInt);//只发送1次
log.info("send:{}", bigInt);
log.info("send字节数:{}", bigInt.toByteArray().length);
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
log.info("发送成功");
}
}
});
}
/**
* <pre>
* 自定义二进制协议:F+4字节长度+具体数值
* 例如:'F',0,0,0,1,42 解码为new BigInteger("42")
* </pre>
*/
@Slf4j
public class BigIntegerDecoder extends ByteToMessageDecoder {
private int splitCount = 0;
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
log.info(">>>>>splitCount:{},可读字节数:{}",++splitCount,in.readableBytes());
//wait until the length prefix is available
if (in.readableBytes() < 5) {
return ;
}
in.markReaderIndex();
int magicNumber = in.readUnsignedByte();
if (magicNumber !='F') {
throw new CorruptedFrameException("Invalid:"+magicNumber);
}
//wait until the whole data is available
int dataLength = in.readInt();
if (in.readableBytes() < dataLength) {
in.resetReaderIndex();
return ;
}
//convert the received data into a new BigInteger
byte [] decoded = new byte[dataLength];
in.readBytes(decoded);
out.add(new BigInteger(decoded));
}
}

客户端发送了1240个字节的BigInteger,服务端接收:

18:15:13.121 [nioEventLoopGroup-3-1] >>>>>splitCount:1,可读字节数:1024
18:15:13.126 [nioEventLoopGroup-3-1] >>>>>splitCount:2,可读字节数:1245

虽然客户端只发送了1次,但服务端分2次接收在BigIntegerDecoder中都接收完后,才调用FactorialServerHandler的channelRead0方法

注意,

in.markReaderIndex();
。。。
if (in.readableBytes() < dataLength) {
in.resetReaderIndex();
return ;
}

以上的流程:

当第一次时,in.readableBytes()=1024,而dataLength=1245,所以进入该方法,将readerIndex复位到之前mark处,此例为0。舍弃该部分包数据。

当第二次时,in.readableBytes()=1245(说明,从0开始读的),读取到了完整的报文。

如果去掉以上代码,则会报错:

18:09:51.465 [nioEventLoopGroup-3-1] >>>>>splitCount:1,可读字节数:1024
io.netty.handler.codec.DecoderException: java.lang.IndexOutOfBoundsException: readerIndex(5) + length(1240) exceeds writerIndex(1024): PooledUnsafeDirectByteBuf(ridx: 5, widx: 1024, cap: 1024)

当然,服务端处理完并把原文发给客户端后,客户端也是分2次读取的:

18:15:13.119 [nioEventLoopGroup-2-1] send字节数:1240
18:15:13.153 [nioEventLoopGroup-2-1] >>>>>splitCount:1,可读字节数:1024
18:15:13.154 [nioEventLoopGroup-2-1] >>>>>splitCount:2,可读字节数:1245

3、通过为每个channel创建新的handler,从而解决即使handler中使用全局变量,也可以避免竞态条件

并发发送数据包,且每个数据包超过1024个字节,如下代码中的成员变量:

public class FactorialServerHandler extends SimpleChannelInboundHandler<BigInteger> {
private BigInteger lastMultiplier = new BigInteger("1");
private BigInteger factorial = new BigInteger("1"
); @Override
protected void channelRead0(ChannelHandlerContext ctx, BigInteger msg) throws Exception {
//计算阶乘并发送到客户端
lastMultiplier = msg;
factorial =
factorial.multiply(msg);
ctx.writeAndFlush(factorial);
}
。。。

客户端调用:

public static void main(String[] args) throws Exception {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
executor(2,Thread.currentThread().getName());// 2*3*4*5=120
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
executor(3,Thread.currentThread().getName());// 3*4*5=60
}
});
t2.start();
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
executor(4,Thread.currentThread().getName());// 4*5=20
}
});
t3.start();
}
public static void executor(int next,String threadName) {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class).handler(new FactorialClientInitializer(next));
// make a new connection
ChannelFuture f = b.connect(HOST, PORT).sync();
// get the handler instance to retrieve the answer.
FactorialClientHandler handler = (FactorialClientHandler) f.channel().pipeline().last();
// print out the answer
log.info("threadName:{},开始:{},结束:{},结果:{}", threadName,next,COUNT, handler.getFactorial());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}

结果:不乱,各自打印各自的。因为,每发送1条数据则创建1个Channel和handler ,所以不会乱。

4、codec是netty封装好了的handler,简化代码开发。

本例中涉及的是:

ByteToMessageDecoder(inbound):必须实现decode方法

MessageToByteEncoder<Number>(outbound):必须实现encode方法

最后,

以上的3参考代码:userguide-04-factorial。1、2参考代码:userguide-04-2-factorial

Netty(4-1)factorial~总结的更多相关文章

  1. 从netty-example分析Netty组件

    分析netty从源码开始 准备工作: 1.下载源代码:https://github.com/netty/netty.git 我下载的版本为4.1 2. eclipse导入maven工程. netty提 ...

  2. Java Netty 4.x 用户指南

    问题 今天,我们使用通用的应用程序或者类库来实现互相通讯,比如,我们经常使用一个 HTTP 客户端库来从 web 服务器上获取信息,或者通过 web 服务来执行一个远程的调用. 然而,有时候一个通用的 ...

  3. netty Getting Started--reference

    reference from:http://docs.jboss.org/netty/3.1/guide/html/start.html 1.1. Before Getting Started 1.2 ...

  4. [转载] Netty源码分析

    转载自http://blog.csdn.net/kobejayandy/article/details/11836813 Netty提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高 ...

  5. netty 入门二 (传输bytebuf 或者pojo)

    基于流的数据传输:在基于流的传输(如TCP / IP)中,接收的数据被存储到套接字接收缓冲器中. 不幸的是,基于流的传输的缓冲区不是数据包的队列,而是字节队列. 这意味着,即使您将两个消息作为两个独立 ...

  6. Netty 4.1 Getting Start (翻译) + Demo

    一.先来官方入门页面的翻译(翻译不好请多包涵) 入门 本章以简单的例子来介绍Netty的核心概念,以便让您快速入门.当您阅读完本章之后,您就能立即在Netty的基础上写一个客户端和一个服务器. 如果您 ...

  7. User guide for Netty 4.x

    Table of Contents Preface The Solution Getting Started Before Getting Started Writing a Discard Serv ...

  8. Netty 介绍

    本指南对Netty 进行了介绍并指出其意义所在. 1. 问题 现在,我们使用适合一般用途的应用或组件来和彼此通信.例如,我们常常使用一个HTTP客户端从远程服务器获取信息或者通过web service ...

  9. netty参考

    前言 问题 现如今我们使用通用的应用程序或者类库来实现系统之间地互相访问,比如我们经常使用一个HTTP客户端来从web服务器上获取信息,或者通过web service来执行一个远程的调用. 然而,有时 ...

随机推荐

  1. [原创]java在线打开PDF文档

    步骤一:(涉及到的工具) 访问:http://www.zhuozhengsoft.com/dowm/,从官网下载PageOffice for Java. 步骤二:(配置工程) 1. 解压PageOff ...

  2. java.lang.NoSuchMethodException: cn.pb.bean.Category.<init>()报错

    代码如下: package cn.pb.bean; import java.util.ArrayList;import java.util.List; /** * 分类的实体类 */public cl ...

  3. java自定义类型 作为HashMap中的Key值 (Pair<V,K>为例)

    由于是自定义类型,所以HashMap中的equals()方法和hashCode()方法都需要自定义覆盖. 不然内容相同的对象对应的hashCode会不同,无法发挥算法的正常功能,覆盖equals方法, ...

  4. 原生js实现Canvas实现拖拽式绘图,支持画笔、线条、箭头、三角形和圆形等等图形绘制功能,有实例Demo

    前言 需要用到图形绘制,没有找到完整的图形绘制实现,所以自己实现了一个 - - 演示地址:查看演示DEMO 新版本支持IE5+(你没看错,就是某软的IE浏览器)以上任意浏览器的Canvas绘图:htt ...

  5. HL7 ADT Message Sample

    http://pixpdqtests.nist.gov:8080/#tests%2Fdriver%2Fversion.htm 可以打开上述连接, 选中version和actor, 然后获取对于samp ...

  6. puppet初始化安装和配置(puppet自动化系列1)

    一.服务器规划 以下均直接yum安装最新版. 服务器操作系统为centos6.2 Puppetmaster1 10.168.32.116 puppstmaster1.jq.com Puppetmast ...

  7. idea 调试技巧1

    1 多线程调试 开发过多线程应用的朋友应该有体会,有些时候,为了观察多个线程间变量的不同状态,以及锁的获取等,就会想到在代码里加个断点debug一下. 在IDE里断点停下来的时候,可以切换到另外的线程 ...

  8. stm32之存储系统

    一.STM32系统结构 要想深刻理解STM32的存储器,需要首先知道STM32的系统结构. 如Figure 1,是STM32系统结构框图. 根据STM32 Reference manual (RM00 ...

  9. [CentOS] 结合Nginx部署DotNetCore的demo项目【转载】

    部署前准备 1.VisualStudio2017+.netcore2.0SDK 2.Centos7.2 3.SecureCRT,Xftp(根据自己喜好) 创建WebApi项目 修改Program.cs ...

  10. solr--搭建全文搜索服务器

    1.下载http://www-eu.apache.org/dist/lucene/solr/6.5.0/ 2.新建: 云模式 3.打开浏览器: 简单的中文分词,暂时是一个一个的分词: