Netty权威指南之NIO通信模型
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通信模型的更多相关文章
- Netty权威指南
Netty权威指南(异步非阻塞通信领域的经典之作,国内首本深入剖析Netty的著作,全面系统讲解原理.实战和源码,带你完美进阶Netty工程师.) 李林锋 著 ISBN 978-7-121-233 ...
- 《Netty权威指南》
<Netty权威指南> 基本信息 作者: 李林锋 出版社:电子工业出版社 ISBN:9787121233432 上架时间:2014-5-29 出版日期:2014 年6月 开本:16开 页码 ...
- 《Netty 权威指南(第2 版)》目录
图书简介:<Netty 权威指南(第2 版)>是异步非阻塞通信领域的经典之作,基于最新版本的Netty 5.0 编写,是国内很难得一见的深入介绍Netty 原理和架构的书籍,也是作者多年实 ...
- netty权威指南学习笔记六——编解码技术之MessagePack
编解码技术主要应用在网络传输中,将对象比如BOJO进行编解码以利于网络中进行传输.平常我们也会将编解码说成是序列化/反序列化 定义:当进行远程跨进程服务调用时,需要把被传输的java对象编码为字节数组 ...
- netty权威指南学习笔记二——netty入门应用
经过了前面的NIO基础知识准备,我们已经对NIO有了较大了解,现在就进入netty的实际应用中来看看吧.重点体会整个过程. 按照权威指南写程序的过程中,发现一些问题:当我们在定义handler继承Ch ...
- Netty权威指南之AIO编程
由JDK1.7提供的NIO2.0新增了异步的套接字通道,它是真正的异步I/O,在异步I/O操作的时候可以传递信号变量,当操作完成后会回调相关的方法,异步I/o也被称为AIO,对应于UNIX网络编程中的 ...
- 《Netty权威指南》(二)NIO 入门
[TOC] 2.1 同步阻塞 I/O 采用 BIO 通信模型的服务器,通常由一个独立的 Acceptor 线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行处理, ...
- Netty权威指南之BIO(Block Input/Output,同步阻塞I/O通信)通信模型
网络编程的基本模型是Client/Server模型,也就是两个进程之间进行相互通信,其中服务端提供位置信息(绑定的IP地址和监听端口),客户端通过连接操作向服务端监听的地址发起连接请求,通过三次握手建 ...
- netty权威指南学习笔记一——NIO入门(1)BIO
公司的一些项目采用了netty框架,为了加速适应公司开发,本博主认真学习netty框架,前一段时间主要看了看书,发现编程这东西,不上手还是觉得差点什么,于是为了加深理解,深入学习,本博主还是决定多动手 ...
随机推荐
- 【异常】IOException parsing XML document from class path resource [xxx.xml]
1.IDEA导入项目运行出现异常 org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing ...
- 启动LINUX系统后,进入图形化界面的命令
1.进入xWindow的命令 $startx回车 或者修改/etc/inittab文件 cd /etc vi inittab 寻找: id:3:initdefault: 改为: id:5:initde ...
- Policy Gradient
Policy Gradient是区别于Q-Learning为代表的value based的方法.policy gradient又可以叫reinforce算法(Williams, 1992). 如今的A ...
- Ubuntu上CUDA环境搭建
1.下载CUDA:https://developer.nvidia.com/cuda-toolkit-archive (如果已经安装了N卡驱动,最好用.deb,如果没有安装,可以用.run) 2.根据 ...
- (笔记)Linux Socket通信:bind: Address already in use
在网络通信时使用Bind绑定IP地址跟端口号时,有时Ctrl+C强制结束进程之后,再次运行程序Bind错误,原因如下: 虽然用Ctrl+C强制结束了进程,但错误依然存在,用netstat -an |g ...
- 64位debian系统下安装inodeClient
linux下的inodeClient下载: 链接:http://pan.baidu.com/s/1jIoX6Zk 密码:vnws 里面包括一份说明书,一个32位的,一个64位的文件: 对于64位的客户 ...
- matlab中的常用的函数——在稀疏表示中学习到的
1, 矩阵的逆: inv()函数: 2. 矩阵的伪逆: pinv()函数: 3. 矩阵的克罗内克尔积: kron()函数: 4. 得到一个dct变换的字典: dctmtx()函数, 它可以得到一个 n ...
- LintCode #3 统计数字
解题思路请参考 代码(可以通过,不过很乱,需要整理): /// <summary> /// 计算n在数组[targetNum]中出现的次数 /// 形如:[0, 1, 2, 3, 4, 5 ...
- char* 与 char[] 的区别
"Hello world"作为静态字符串实际上存储在数据区,但写程序的人不知道这个地址,而程序本身知道.当某一函数以{ char p[] = "Hello world&q ...
- C# 中base和this关键字
base: 用于在派生类中实现对基类公有或者受保护成员的访问,但是只局限在构造函数.实例方法和实例属性访问器中. MSDN中小结的具体功能包括: ()调用基类上已被其他方法重写的方法. ()指定创建派 ...