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框架,前一段时间主要看了看书,发现编程这东西,不上手还是觉得差点什么,于是为了加深理解,深入学习,本博主还是决定多动手 ...
随机推荐
- Maven打包生成源码包和Javadoc包
https://blog.csdn.net/top_code/article/details/53586551 当我们开发了一个公共模块,将它deploy到Maven仓库时,最好同时提供源码包和Jav ...
- Java设计模式(19)状态模式(State模式)
State的定义:不同的状态,不同的行为:或者说,每个状态有着相应的行为. 何时使用状态模式 State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If else ...
- 《Scrum实战》读书会作业01 - 用知行视角总结现在或者过去的一个项目
下面是<Scrum实战>读书会的第1个作业,主要是用知行视角来总结回顾现在或者过去的一个项目. 项目背景 2011年初,我做的项目是一个搜索引擎相关的项目,这个项目为公司在全球范围内的金融 ...
- 【转】jmeter 如何将上一个请求的结果作为下一个请求的参数——使用正则提取器
1.简介 Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试,它最初被设计用于Web应用测试但后来扩展到其他测试领域. 它可以用于测试静态和动态资源例如 ...
- Sword pcre库函数学习三
14.pcre_get_substring_list 原型: #include <pcre.h> int pcre_get_substring_list(const char *subje ...
- Linux 下用管道执行 ps aux | grep 进程ID 来获取CPU与内存占用率
#include <stdio.h> #include <unistd.h> int main() { char caStdOutLine[1024]; // ps ...
- tensorflow prelu的实现细节
tensorflow prelu的实现细节 output = tf.nn.leaky_relu(input, alpha=tf_gamma_data,name=name) #tf.nn.leaky_r ...
- Couchbase 集群小实践
局域网 两台机 192.168.1.2 我们称为A机器 192.168.1.3 我们称为B机器 配置集群的时候,从A或者是B的web后台都可以添加, 在这里 我们以 A机器为主 目 ...
- perl 安装Image::Magick 模块
ImageMagick 是一个处理图片的库,有C, perl, python, java 等多种语言对应的库 在安装perl 对应的Image::Magick 模块之前,首先需要安装 ImgeMagi ...
- Springmvc 的post请求的json格式参数
背景: 这两天在项目中遇到了一个问题.我的环境是springmvc4.1.9,写了几个可以用ajax请求的接口(ajax.jsonp 调用正常).突然一时兴起就用 HTTP 请求的工具(比如火狐浏览器 ...