一、NIO

  Netty框架底层是对NIO的高度封装,所以想要更好的学习Netty之前,应先了解下什么是NIO - NIO是non-blocking的简称,
在jdk1.4 里提供的新api,他的他的特性如下:
  * 为所有的原始类型提供(Buffer)缓存支持,字符集编码解码解决方案。
  * Channel :一个新的原始I/O 抽象。支持锁和内存映射文件的文件访问接口。提供多路(non-bloking)非阻塞式的高伸缩
性网络I/O 。
  NIO是一个非阻塞式的I/O,它由一个专门的线程来处理所有的IO事件,并负责分发,并且它只有在事件到达的时候才会触发,
而不是去同步的监视事件;线程之间通过wait,notify 等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换。
  NIO和IO最大的区别是数据打包和传输方式。IO是以流的方式处理数据,而NIO是以块的方式处理数据。NIO的核心部分由
Channels、Buffers、Selectors三部分组成。

(一)Channel和Buffer

  正常的情况下,所有的IO在NIO中都从一个Channel 开始。Channel有点像流。数据可以从Channel读到Buffer中,也可以从
Buffer写到Channel中。JAVA NIO中的一些主要Channel的实现:FileChannel、DatagramChannel、SocketChannel、
ServerSocketChannel。这些实现类覆盖了UDP和TCP网络IO,以及文件IO。
  而Buffer的一些实现类:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer,则覆盖
了能通过IO发送的基本数据类型:byte,short,int,long,float,double和char。

(二)Selector

  Selector允许单线程处理多个Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector
就会很方便。而要使用Selector,就得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的
通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

二、Netty

(一)Netty入门

  大数据,高访问场景的互联网项目或者多系统的协同工作,使用一个服务器根本不能胜任。就需要把系统拆分成了多个服务,
根据需要部署在多个机器上,这些服务非常灵活,可以随着访问量弹性扩展。但是多个模块的跨服务通信,时间和资源都是极大
地浪费。传统的Blocking IO不能解决,因为会有线程阻塞的问题,而使用非阻塞IO(NIO),则需要耗费太多的精力。而Netty框架
(RPC框架)则很好的解决了这个问题。
  Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、
高可靠性的网络服务器和客户端程序。他就是一个程序,是封装java socket noi的,我们直接拿来用就好了。
  Netty通信服务端的步骤:
    1、创建两个NIO线程组,一个专门用于网络事件处理(接受客户端的连接),另一个则进行网络通信的读写。
    2、创建一个ServerBootstrap对象,配置Netty的一系列参数,例如接受传出数据的缓存大小等。
    3、创建一个用于实际处理数据的类ChannelInitializer,进行初始化的准备工作,比如设置接受传出数据的字符集、
格式以及实际处理数据的接口。
    4、绑定端口,执行同步阻塞方法等待服务器端启动即可。
    5、关闭相应的资源。

服务端栗子:
  服务端的管理者:

/**
* 服务端处理通道.这里只是打印一下请求的内容,并不对请求进行任何的响应
* 继承自ChannelHandlerAdapter, 这个类实现了ChannelHandler接口,
* ChannelHandler提供了许多事件处理的接口方法,然后你可以覆盖这些方法。
* @author lcy
*
*/
public class DiscartServiceHandler extends ChannelHandlerAdapter {
/**
* 客户端收到新消息时,这个方法会被调用
*
* @param ctx
* 通道处理的上下文信息
* @param msg
* 接受的消息
*/
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
// 将接收到的信息转换为缓冲区
ByteBuf str = (ByteBuf) msg;
// 打印传输过来的信息
System.out.print(str.toString(CharsetUtil.UTF_8));
} finally {
// 释放ByteBuf对象
ReferenceCountUtil.release(msg);
}
} /**
* 在异常时触发
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//输出错误信息
cause.printStackTrace();
ctx.close();
}
}

服务端:

/**
* 服务端
* @author lcy
*
*/
public class DiscartServer {
private int port; public DiscartServer(int port) {
super();
this.port = port;
} public void run() throws Exception {
//(一)设置两个线程组
//用来接收进来的连接
NioEventLoopGroup bossGroup = new NioEventLoopGroup();
// 用来处理已经接受的连接
NioEventLoopGroup workGroup = new NioEventLoopGroup();
System.out.println("准备运行的端口" + port);
try {
//(二)辅助工具类,用于服务器通道的一系列配置
ServerBootstrap bootstrap = new ServerBootstrap();
//(三)绑定两个线程组
// 设置group,这一步是必须的,如果没有设置group将会报java.lang.IllegalStateException:group not set异常
bootstrap = bootstrap.group(bossGroup, workGroup);
//(四)指定NIO的模式
/***
* ServerSocketChannel以NIO的selector为基础进行实现的,用来接收新的连接
* 这里告诉Channel如何获取新的连接.
*/
bootstrap = bootstrap.channel(NioServerSocketChannel.class);
//(五)配置具体的数据处理方式,就是往里添加规则
bootstrap = bootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override
protected void initChannel(SocketChannel arg0) throws Exception {
//与50秒内都没有与服务端进行通信的客户端断连
arg0.pipeline().addLast(new ReadTimeoutHandler(50));
arg0.pipeline().addLast(new HttpObjectAggregator(1048576));
// 添加实际处理数据的类
arg0.pipeline().addLast(new DiscartServiceHandler());
}
});
//(六)设置TCP缓冲区
bootstrap = bootstrap.option(ChannelOption.SO_BACKLOG, 128);
//保持连接
bootstrap = bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
//(七)绑定端口,启动接收进来的连接
ChannelFuture sync = bootstrap.bind(port).sync();
//(八) 这里会一直等待,直到socket被关闭
sync.channel().closeFuture().sync();
} finally {
//(九)关闭资源
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
//服务开启
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new DiscartServer(port).run();
System.out.println("server:run()");
}
}

客户端栗子:

实际处理数据的类:

public class ChannelClient extends ChannelInitializer{

@Override
protected void initChannel(Channel arg0) throws Exception {
//与50秒内都没有与服务端进行通信的客户端断连
arg0.pipeline().addLast(new ReadTimeoutHandler(50));
arg0.pipeline().addLast(new HttpObjectAggregator(1048576));
//设置Channel
arg0.pipeline().addLast(new ChannelHandlerAdapter(){ @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
// 将接收到的信息转换为缓冲区
ByteBuf str = (ByteBuf) msg;
// 打印传输过来的信息
System.out.print(str.toString(CharsetUtil.UTF_8));
} finally {
// 释放ByteBuf对象
ReferenceCountUtil.release(msg);
}
} @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//输出错误信息
cause.printStackTrace();
ctx.close();
}
}); }
}

客户端:

/**
* 客户端
* @author lcy
*/
public class Client {
@SuppressWarnings("resource")
public static void main(String[] args) throws Exception {
//创建一个新的线程组
NioEventLoopGroup workGroup = new NioEventLoopGroup();
//初始化Netty
Bootstrap bootstrap = new Bootstrap();
//指定工作的线程组
bootstrap = bootstrap.group(workGroup);
//指定 Channel的类型。因为是客户端, 因此使用了 NioSocketChannel。
bootstrap.channel(NioSocketChannel.class);
/**
* 设置链接的一些属性
*/
//降低延迟,禁用了禁用nagle算法。nagle算法受TCP延迟确认影响,会导致相继两次向连接发送请求包。
bootstrap.option(ChannelOption.TCP_NODELAY, true);
//保持连接检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
//使用netty默认的解码器会出现读取不完整,不会执行channelRead方法。设置这个属性可惜保证Netty读取的完整
bootstrap.option(ChannelOption.MAX_MESSAGES_PER_READ, Integer.MAX_VALUE);
//设置数据处理器
bootstrap.handler(new ChannelClient());
//同步的链接
Channel channel = bootstrap.connect("127.0.0.1", 8080).sync().channel();
channel.writeAndFlush(Unpooled.copiedBuffer("Hello Netty...".getBytes()));
channel.closeFuture().sync();
workGroup.shutdownGracefully();
}
}

(二)Netty的数据通讯:

  1、使用长连接通道不断开的形式进行通信,也就是服务器和客户端的通道一直处于开启状态,如果服务器性能足够好,
并且客户端数量也比较多的情况下,推荐这种方式。
  2、一次性批量提交数据,采用短连接方式。也就是说先把数据保存到本地临时缓存区或者临时表,当达到界值时进行一
次性批量提交,又或者根据定时任务轮询提交。
  3、使用一种特殊的长连接,在某一指定时间段内,服务器与某台客户端没有任何通信,则断开连接。下次连接则是客户
端向服务器发送请求的时候,再次建立连接。

(三)Netty的编解码器:  

    1. Decoder 解码器    负责将消息从字节或其他序列形式转成指定的消息对象。
  2. Encoder 编码器    将消息对象转成字节或其他序列形式在网络上传输。
  入站”ByteBuf读取bytes后由 ToIntegerDecoder 进行解码,然后将解码后的消息存入List集合中,然后传递到ChannelPipeline
中的下一个ChannelInboundHandler。
  解码器:
    1)ByteToMessageDecoder,需自己判断ByteBuf读取前是否有足够的字节,否则会出现沾包的现象。
    2)ReplayingDecoder,无需自己检查字节长度,但是使用起来具有局限性:
      * 不是所有的操作都被ByteBuf支持,如果调用一个不支持的操作会抛出DecoderException。
      * ByteBuf.readableBytes()大部分时间不会返回期望值。
    3)MessageToMessageDecoder(message-to-message)
  解码器是用来处理入站数据,Netty提供了很多解码器的实现,可以根据需求详细了解。
  编码器:
    1)MessageToByteEncoder
    2)MessageToMessageEncoder 需要将消息编码成其他的消息时可以使用Netty提供的MessageToMessageEncoder抽象类
来实现。例如将Integer编码成String。

(四)Netty中解决TCP粘包/拆包问题

  想要解决TCP的粘包/拆包问题,首先要知道什么是TCP粘包、拆包:
    TCP是一个“流”协议,所谓流就是没有界限的遗传数据。大家可以想象一下,如果河水就好比数据,他们是连成一片的,没有
分界线,TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的具体情况进行包的划分,也就是说,在业务上一个完整的
包可能会被TCP分成多个包进行发送,也可能把多个小包封装成一个大的数据包发送出去,这就是所谓的粘包/拆包问题。
  解决方案:
  1、消息定长,例如每个报文的大小固定为200个字节,如果不够,空位补空格。
  2、在包尾部增加特殊字符进行分割,例如加回车等。
  3、将消息分为消息头和消息体,在消息头中包含表示消息总长度的字段,然后进行业务逻辑的处理。
  Netty中解决TCP粘包/拆包的方法:
    1、分隔符类:DelimiterBasedFrameDecoder(自定义分隔符)
    2、定长:FixedLengthFrameDecoder

Netty入门的更多相关文章

  1. Netty入门之客户端与服务端通信(二)

    Netty入门之客户端与服务端通信(二) 一.简介 在上一篇博文中笔者写了关于Netty入门级的Hello World程序.书接上回,本博文是关于客户端与服务端的通信,感觉也没什么好说的了,直接上代码 ...

  2. Netty入门之HelloWorld

    Netty系列入门之HelloWorld(一) 一. 简介 Netty is a NIO client server framework which enables quick and easy de ...

  3. netty入门(一)

    1. netty入门(一) 1.1. 传统socket编程 在任何时候都可能有大量的线程处于休眠状态,只是等待输入或者输出数据就绪,这可能算是一种资源浪费. 需要为每个线程的调用栈都分配内存,其默认值 ...

  4. Netty入门(三)之web服务器

    Netty入门(三)之web服务器 阅读前请参考 Netty入门(一)之webSocket聊天室 Netty入门(二)之PC聊天室 有了前两篇的使用基础,学习本文也很简单!只需要在前两文的基础上稍微改 ...

  5. Netty入门(二)之PC聊天室

    参看Netty入门(一):Netty入门(一)之webSocket聊天室 Netty4.X下载地址:http://netty.io/downloads.html 一:服务端 1.SimpleChatS ...

  6. Netty入门(一)之webSocket聊天室

    一:简介 Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能.高可靠性协议的服务器和客户端. 换句话说,Netty 是 ...

  7. netty同时做http和websocket(netty入门)

    ---恢复内容开始--- http://www.jianshu.com/p/5c29c6c6d28c ---恢复内容结束--- http://www.jianshu.com/p/5c29c6c6d28 ...

  8. Netty入门教程——认识Netty

    什么是Netty? Netty 是一个利用 Java 的高级网络的能力,隐藏其背后的复杂性而提供一个易于使用的 API 的客户端/服务器框架. Netty 是一个广泛使用的 Java 网络编程框架(N ...

  9. Netty 系列(三)Netty 入门

    Netty 系列(三)Netty 入门 Netty 是一个提供异步事件驱动的网络应用框架,用以快速开发高性能.高可靠性的网络服务器和客户端程序.更多请参考:Netty Github 和 Netty中文 ...

随机推荐

  1. Android学习之Animation(一)

    3.0以前,android支持两种动画模式,Tween Animation,Frame Animation,在android3.0中又引入了一个新的动画系统:Property Animation,这三 ...

  2. python multiprocessing example

    python multiprocessing example Server Code: #!/usr/bin/python #-*- coding: UTF-8 -*- # mpserver.py # ...

  3. 【FPGA学习】Verilog之加法器

    在fpga工程应用设计中,随处可见加法器,乘法器等等.现在将一些常用模块和心得体会先记录下来,以便日后使用. 一位半加器: module halfadder(cout,sum,a,b); output ...

  4. Android Studio查看应用数字签名-android学习之旅(76)

    Android Studio和Eclispe还是有比较大的区别,在这地方,eclipse可以直接在设置里面,而AS就需要通过Terminal来查看 步骤 1.首先定位到.android 一般都是在C盘 ...

  5. 【一天一道LeetCode】#68. Text Justification

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...

  6. [案例]某体育用品公司在零售领域BI的产品应用解决方案

    随着某体育用品公司集团经营规模的不断扩大,信息化的建设也在不断的深入,从POS系统到ERP系统,从MAIL系统到OA系统,整个集团的每项工作都与信息系统密不可分,可以说是行业内信息化建设的先导者.但是 ...

  7. 《java入门第一季》之类StringBuffer类初步

    /* * 线程安全(多线程分析) * 安全 -- 同步 -- 数据是安全的 * 不安全 -- 不同步 -- 效率高一些 * 安全和效率问题是永远困扰我们的问题. * 安全:医院的网站,银行网站 * 效 ...

  8. volley请求原理

    Volley 实现原理解析 本文为 Android 开源项目实现原理解析 中 Volley 部分 项目地址:Volley,分析的版本:35ce778,Demo 地址:Volley Demo 分析者:g ...

  9. 如何成为Android高手

    要成为Android 高手并不是一件容易的事情.并不是很多人想象的 能够飞快的写出几行漂亮的代码去解决一些困难的问题 就是Android 高手了.真正的Android 高手需要考虑的问题远远不是写些漂 ...

  10. Android 之dragger使用

    1.依赖的注入和配置独立于组件之外,注入的对象在一个独立.不耦合的地方初始化,这样在改变注入对象时,我们只需要修改对象的实现方法,而不用大改代码库. 2.依赖可以注入到一个组件中:我们可以注入这些依赖 ...