NIO简介:与Socket和ServerSocket类相对应,NIO提供了SocketChannel和ServerSocketChannel两种不同的套接字通道实现,这两种新通道都支持阻塞和非阻塞两种模式。阻塞模式使用简单,但是性能和可靠性不好,非阻塞模式正好相反。

  1.缓冲区Buffer:一个对象,包含一些要写入或要读出的数据。任何时候访问NIO中数据,都是通过缓冲区进行操作。缓冲区实质是一个数据,最常用的缓冲区是ByteBuffer。每一种Java基本类型都对应一种缓冲区,如ByteBuffer字节缓冲区、CharBuffer字符缓冲区、ShortBuffer短整型缓冲区、IntBuffer整型缓冲区、LongBuffer长整型缓冲区、FloatBuffer浮点型缓冲区、DoubleBuffer双精度浮点型缓冲区

  2.通道Channel:Channel是一个通道,网络数据通过Channel读取和写入。Channel可以用于读取和写入同时进行,因为通道是全双工的,而流只是在一个方向上移动(一个流必须是InputStream或者OutputStream的子类)。Channel可以分为两大类,用于网络读写的SelectableChannel和用于文件操作的FileChannel

  3.多路复用器Selector:Selector会不断的轮询注册在其上的Channel,如果某个Channel上面发生读或者写事件,这个Channel就处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取就绪Channel的集合,进行后续的I/O操作。一个多路复用器Selector可以同时轮询多个Channel,由于JDK使用了epoll()代替传统的select实现,所以它并没有最大连接句柄1024/2048的限制,意味着一个线程负责Selector的轮询,就可以接入成千上万的客户端

如果发送区TCP缓冲区满,会导致写半包,此时,需要注册监听写操作位,循环写,直到整包消息写入TCP缓冲区

Server对应Accept,Client对应Connection

package com.hjp.netty.nio;

import java.io.IOException;

public class TimeServer {

    public static void main(String[] args) throws IOException {
int port = 8080;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(port);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
//创建多路复用类,一个独立线程,负责轮询多路复用器Selector,可以处理多个客户端的并发接入
MultiplexerTimeServer timeServer = new MultiplexerTimeServer(port);
new Thread(timeServer, "NIO-MultiplexerTimeServer-001").start();
} }

TimeServer

package com.hjp.netty.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
import java.util.Set; public class MultiplexerTimeServer implements Runnable { private Selector selector; private ServerSocketChannel serverChannel; private volatile boolean stop; /**
* 初始化多路复用器,绑定监听端口
*
* @param port
*/
public MultiplexerTimeServer(int port) {
try {
selector = Selector.open();
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port), 1024);
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("The time server is start in port : " + port);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
} public void stop() {
this.stop = true;
} @Override
public void run() {
while (!stop) {
try {
selector.select(1000);//休眠一秒
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
SelectionKey key = null;
while (it.hasNext()) {
key = it.next();
it.remove();
try {
handleInput(key);
} catch (Exception e) {
if (key != null) {
key.cancel();
if (key.channel() != null) {
key.channel().close();
}
}
}
}
} catch (Throwable t) {
t.printStackTrace();
}
}
//多路复用器关闭后,所有注册在上面的Channel和Pipe等资源都会被自动去注册并关闭,所有不需要重复释放资源
if (selector != null) {
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} private void handleInput(SelectionKey key) throws IOException {
if (key.isValid()) {
//处理新接入的请求消息
if (key.isAcceptable()) {
//Accept the new connection
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
//Add the new connection to the selector
sc.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
//Read the data
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(readBuffer);
if (readBytes > 0) {
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("The time server receive order : " + body);
String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(System.currentTimeMillis()).toString() : "BAD ORDER";
doWrite(sc, currentTime);
} else if (readBytes < 0) {
//对端链路关闭
key.cancel();
sc.close();
} else {
;//读到0字节,忽略
}
}
}
} private void doWrite(SocketChannel channel, String response) throws IOException {
if (response != null && response.trim().length() > 0) {
byte[] bytes = response.getBytes();
ByteBuffer writeBuffer=ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);
writeBuffer.flip();
channel.write(writeBuffer);
}
}
}

多路复用器类

package com.hjp.netty.nio;

import java.io.IOException;

public class TimeClient {

    public static void main(String[] args) throws IOException {
int port = 8080;
if (args != null && args.length > 0) {
try {
port = Integer.valueOf(args[0]);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
new Thread(new TimeClientHandler("127.0.0.1",port),"TimeClient-001").start();
} }

TimeClient

package com.hjp.netty.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set; public class TimeClientHandler implements Runnable { private String host;
private int port;
private Selector selector;
private SocketChannel socketChannel;
private volatile boolean stop; public TimeClientHandler(String host, int port) {
this.host = host == null ? "127.0.0.1" : host;
this.port = port;
try {
selector = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
} @Override
public void run() {
try {
doConnect();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
while (!stop) {
try {
selector.select(1000);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
SelectionKey key = null;
while (it.hasNext()) {
key = it.next();
it.remove();
try {
handleInput(key);
} catch (Exception e) {
if (key != null) {
key.cancel();
if (key.channel() != null) {
key.channel().close();
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
//多路复用器关闭后,所有注册在上面的Channel和Pipe等资源都会被自动去注册并关闭,所以不需要重复释放资源
if (selector != null) {
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} private void handleInput(SelectionKey key) throws IOException {
if (key.isValid()) {
//判断是否连接成功
SocketChannel sc = (SocketChannel) key.channel();
if (key.isConnectable()) {
if (sc.finishConnect()) {
sc.register(selector, SelectionKey.OP_READ);
doWrite(sc);
} else {
System.exit(1);//连接失败进程退出
}
}
if (key.isReadable()) {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(readBuffer);
if (readBytes > 0) {
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("Now is : " + body);
this.stop = true;
} else if (readBytes < 0) {
//对端链路关闭
key.cancel();
sc.close();
} else {
;//对到0字节,忽略
}
}
}
} private void doConnect() throws IOException {
//如果直接连接成功,则注册到多路复用器上,发送请求消息,读应答
if (socketChannel.connect(new InetSocketAddress(host, port))) {
socketChannel.register(selector, SelectionKey.OP_READ);
doWrite(socketChannel);
} else {
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
} private void doWrite(SocketChannel sc) throws IOException {
byte[] req = "QUERY TIME ORDER".getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(req.length);
writeBuffer.put(req);
writeBuffer.flip();
sc.write(writeBuffer);
if (!writeBuffer.hasRemaining()) {
System.out.println("Send order 2 server succeed.");
}
}
}

TimeClientHandler

Netty权威指南之NIO通信模型的更多相关文章

  1. Netty权威指南

    Netty权威指南(异步非阻塞通信领域的经典之作,国内首本深入剖析Netty的著作,全面系统讲解原理.实战和源码,带你完美进阶Netty工程师.) 李林锋 著   ISBN 978-7-121-233 ...

  2. 《Netty权威指南》

    <Netty权威指南> 基本信息 作者: 李林锋 出版社:电子工业出版社 ISBN:9787121233432 上架时间:2014-5-29 出版日期:2014 年6月 开本:16开 页码 ...

  3. 《Netty 权威指南(第2 版)》目录

    图书简介:<Netty 权威指南(第2 版)>是异步非阻塞通信领域的经典之作,基于最新版本的Netty 5.0 编写,是国内很难得一见的深入介绍Netty 原理和架构的书籍,也是作者多年实 ...

  4. netty权威指南学习笔记六——编解码技术之MessagePack

    编解码技术主要应用在网络传输中,将对象比如BOJO进行编解码以利于网络中进行传输.平常我们也会将编解码说成是序列化/反序列化 定义:当进行远程跨进程服务调用时,需要把被传输的java对象编码为字节数组 ...

  5. netty权威指南学习笔记二——netty入门应用

    经过了前面的NIO基础知识准备,我们已经对NIO有了较大了解,现在就进入netty的实际应用中来看看吧.重点体会整个过程. 按照权威指南写程序的过程中,发现一些问题:当我们在定义handler继承Ch ...

  6. Netty权威指南之AIO编程

    由JDK1.7提供的NIO2.0新增了异步的套接字通道,它是真正的异步I/O,在异步I/O操作的时候可以传递信号变量,当操作完成后会回调相关的方法,异步I/o也被称为AIO,对应于UNIX网络编程中的 ...

  7. 《Netty权威指南》(二)NIO 入门

    [TOC]   2.1 同步阻塞 I/O 采用 BIO 通信模型的服务器,通常由一个独立的 Acceptor 线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行处理, ...

  8. Netty权威指南之BIO(Block Input/Output,同步阻塞I/O通信)通信模型

    网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建 ...

  9. netty权威指南学习笔记一——NIO入门(1)BIO

    公司的一些项目采用了netty框架,为了加速适应公司开发,本博主认真学习netty框架,前一段时间主要看了看书,发现编程这东西,不上手还是觉得差点什么,于是为了加深理解,深入学习,本博主还是决定多动手 ...

随机推荐

  1. 关于javascrpt if快速判断说明

    在js中有如下值能作为if的条件判断: 在js中有6种类型 undefined.null.string.number.boolean和object  object是对象类型 它们在逻辑运算符里有对应的 ...

  2. SSh on Ubuntu

    ssh程序分为有客户端程序openssh-client和服务端程序openssh-server.如果需要ssh登陆到别的电脑,需要安装openssh-client,该程序Ubuntu是默认安装的.而如 ...

  3. Airtest 网易 UI 自动化工具 Airtest 浅用记录

    一 使用目的 该工具主要是面向游戏UI测试基于图像识别,如游戏框架unity,Cocos-js以及网易内部的游戏框架同时也支持原生Android App 的基于元素识别的UI自动化测试.本文主要使用目 ...

  4. Self_Java + Selenium + Maven 环境搭建步骤

    转自:http://www.jianshu.com/p/3c05e8c9ee81 我们使用Java+Selenium WebDriver 来进行环境的搭建,同样分为两个部分: 安装Java 和 int ...

  5. 产品需求文档 PRD

    第一轮: 1,文档使用方:UI设计师 2.内容:       根据战略层定义出来产品功能范围,       说明此产品的目的,方便UI设计人员更好的理解产品       产品基本流程       详细 ...

  6. 用python做数字油画或者从一幅画学习风格,去画另一幅画

    1. 用python做数字油画 模块: pillow 2. 从一幅画学习风格,去画另一幅画 http://pytorch.org/tutorials/advanced/neural_style_tut ...

  7. VIM命令行模式下的相关知识

    当按下:键时, Vim会切换到命令行模式: 重复上次执行的EX命令,只需按@:就可以: 在一行或多个连续行执行命令 很多的EX全天候可以指定操作范围,如行号,位置标记等: 以:print命令作为演示, ...

  8. HTML5游戏中动画帧的概念理解

    最近在弄一个HTML5游戏,在学习过程中,总结出这个帧结构. HTML5游戏最重要也就是对帧的理解. 容器:Canvas 一个画布 sprite:一个canvas上有多个动画,每个动画对象就是一个An ...

  9. Native Code

    Native Code — 本机代码,即已被编译为运行于特定处理器机器码的代码.        本地代码(native code)是计算机编程(代码),编译用来运行一个特殊的处理器(如英特尔x86级的 ...

  10. C#常用数据类型间的转换

    数据类型有很多种,数据类型间的转换也是有很多的方法,如果不细心整理的话等到用的时候再查就会显得很浪费时间,所以决心整理出这篇博文.主要是讲解常用数据类型之间的转换方法以及常见数据类型所占字节数. 字节 ...