第一章 java nio三大组件与使用姿势
本案例来源于《netty权威指南》
一、三大组件
- Selector:多路复用器。轮询注册在其上的Channel,当发现某个或者多个Channel处于“就绪状态”后(accept接收连接事件、connect连接完成事件、read读事件、write写事件),从阻塞状态返回就绪的Channel的SelectionKey集合,之后进行IO操作。
- Channel:封装了socket。
- ServerSocketChannel:封装了ServerSocket,用于accept客户端连接请求;
- SocketChannel:一对SocketChannel组成一条虚电路,进行读写通信
- Buffer:用于存取数据,最主要的是ByteBuffer
- position:下一个将被操作的字节位置
- limit:在写模式下表示可以进行写的字节数,在读模式下表示可以进行读的字节数
- capacity:Buffer的大小
二、服务端代码
1、服务端启动类
public class Server {
public static void main(String[] args) throws IOException {
new Thread(new ServerHandler(), "server-1").start();
}
}
创建一个任务ServerHandler,然后创建一条线程,启动执行该任务。
2、逻辑处理类
public class ServerHandler implements Runnable {
private Selector selector;
private ServerSocketChannel ssChannel; public ServerHandler(int port) {
try {
//等价于 Selector selector = SelectorProvider.provider().openSelector();
selector = Selector.open();
//等价于 SelectorProvider.provider().openServerSocketChannel()
ssChannel = ServerSocketChannel.open();
ssChannel.configureBlocking(false);
ssChannel.bind(new InetSocketAddress(port), 1024);
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
} public void run() {
for (; ; ) {
try {
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
handleInput(key);
}
} catch (Throwable t) {
t.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 java.util.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);
}
}
}
步骤:
1、创建一个Selector和ServerSocketChannel实例
2、配置ServerSocketChannel实例为非阻塞
3、ServerSocketChannel实例bind端口
4、将ServerSocketChannel实例注册到selector上,监听OP_ACCEPT事件
下面的任务在Server创建的新的线程中执行,不影响主线程执行其他逻辑
5、之后进入死循环
5.1、使用select.select()阻塞等待就绪事件(这里是等待OP_ACCEPT事件),一旦有有就绪事件到达,立即向下执行
5.2、使用selector.selectedKeys()获取已经就绪的SelectionKey(即OP_ACCEPT/OP_CONECT/OP_READ/OP_WRITE)集合,之后循环遍历
5.3、从迭代器删除该SelectionKey,防止下一次再被遍历到
5.4、如果SelectionKey==OP_ACCEPT,则通过ServerSocketChannel.accept()创建SocketChannel,该SocketChannel是后续真正的与客户端的SocketChannel进行通信的实体
5.5、配置新创建的SocketChannel实例为非阻塞,然后将该SocketChannel实例注册到selector实例上,监听OP_READ事件
5.6、等客户端发出请求数据时,此处监听到SelectionKey==OP_READ,则创建ByteBuffer实例,将SocketChannel中的数据读取到ByteBuffer中,然后再创建ByteBuffer将信息写回到SocketChannel(也就是说数据的读写一定要通过Buffer)
三、客户端代码
1、客户端启动类
public class Client {
public static void main(String[] args) {
new Thread(new ClientHandler("127.0.0.1", 8080), "client-1").start();
}
}
创建一个任务ClientHandler,然后创建一条线程,启动执行该任务。
2、逻辑处理类
public class ClientHandler implements Runnable {
private String host;
private int port; private Selector selector;
private SocketChannel socketChannel; public ClientHandler(String host, int port) {
this.host = host;
this.port = port;
try {
selector = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
} public void run() {
try {
doConnect();
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
while (true) {
try {
selector.select();
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
handleInput(key);
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
} 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);// 连接失败,进程退出
} else 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);
} 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.");
}
}
步骤:
1、创建一个Selector和SocketChannel实例
2、配置SocketChannel实例为非阻塞
下面的任务在Cilent创建的新的线程中执行,不影响主线程执行其他逻辑
3、SocketChannel.connect连接到server端,如果连接没有马上成功,将该SocketChannel实例注册到selector上,监听OP_CONNECT事件;如果连接成功,将该SocketChannel实例注册到selector上,监听OP_READ事件,之后写数据给server端
4、之后进入死循环
4.1、使用select.select()阻塞等待就绪事件,一旦有有就绪事件到达,立即向下执行
4.2、使用selector.selectedKeys()获取已经就绪的SelectionKey(即OP_ACCEPT/OP_CONECT/OP_READ/OP_WRITE)集合,之后循环遍历
4.3、从迭代器删除该SelectionKey,防止下一次再被遍历到
4.4、如果SelectionKey==OP_CONNECT,将该SocketChannel实例注册到selector上,监听OP_READ事件,之后写数据给server端;如果监听到SelectionKey==OP_READ,则创建ByteBuffer实例,将SocketChannel中的数据读取到ByteBuffer中
第一章 java nio三大组件与使用姿势的更多相关文章
- Java NIO 三大组件之 Channel
Java NIO 之 Channel 一.什么是Channel Channel用于源节点(例如磁盘)与目的节点的连接,它可以进行读取,写入,映射和读/写文件等操作. 在Java NIO中负责缓冲区中数 ...
- Java NIO 三大组件之 Buffer
NIO大三组件 之Buffer 一.什么是Buffer Buffer是用于特定原始类型的数据的容器. 它的实质就是一组数组,用于存储不同类型的数据. 二.缓冲区的类型 缓冲区类型除了Boolean值类 ...
- java web(五):java web三大组件之另外两个和八大监听器
java的三大组件指Servlet.Filter.Listener.八大监听器指八个接口.前面介绍了Servlet,现在介绍一下Filter拦截器以及拦截地址的设置, Listener监听那些事件. ...
- 第一章 Java的I/O演进之路
I/O基础入门 Java的I/O演进 第一章 Java的I/O演进之路 1.1 I/O基础入门 1.1.1 Linux网络I/O模型简介 根据UNIX网络编程对I/O模型的分类,UNIX提供了5中I/ ...
- Java基础知识二次学习-- 第一章 java基础
基础知识有时候感觉时间长似乎有点生疏,正好这几天有时间有机会,就决定重新做一轮二次学习,挑重避轻 回过头来重新整理基础知识,能收获到之前不少遗漏的,所以这一次就称作查漏补缺吧!废话不多说,开始! 第一 ...
- javaSE习题 第一章 JAVA语言概述
转眼就开学了,正式在学校学习SE部分,由于暑假放视频过了一遍,略感觉轻松,今天开始,博客将会记录我的课本习题,主要以文字和代码的形式展现,一是把SE基础加强一下,二是课本中有很多知识是视频中没有的,做 ...
- 第一章 –– Java基础语法
第一章 –– Java基础语法 span::selection, .CodeMirror-line > span > span::selection { background: #d7d4 ...
- 第一章 java基本多线程技能
第一章 java多线程技能 1 线程:进程是操作系统结构的基础,是一次程序的执行,是一个程序及其数据在处理顺序时发生的活动:是程序在一个数据集合上运行的过程,他是系统进行资源分配和调度的一个独立单位. ...
- java web 三大组件
JavaWeb三大组件 Servlet,Filter,Listener. Servlet Servlet的作用 在Java web b/s架构中,servlet扮演了重要的角色,作为一个中转处理的容器 ...
随机推荐
- Consumer is not subscribed to any topics or assigned any partitions
版本: scala:2.11.8 spark:2.11 hbase:1.2.0-cdh5.14.0 报错信息: java.lang.IllegalStateException: Consumer is ...
- Codeforces 513E2 Subarray Cuts dp (看题解)
我们肯定要一大一小间隔开来所以 把式子拆出来就是类似这样的形式 s1 - 2 * s2 + 2 * s3 + ...... + sn 然后把状态开成四个, 分别表示在顶部, 在底部, 在顶部到底部的中 ...
- web移动端浮层滚动阻止window窗体滚动JS/CSS处理
CSS代码: .noscroll, .noscroll body { overflow: hidden; } .noscroll body { position: relative; } JS代码: ...
- 003 使用SpringMVC开发restful API--查询用户
一:介绍说明 1.介绍 2.restful api的成熟度 二:编写Restful API的测试用例 1.引入spring的测试框架 在effective pom中查找 2.新建测试包,测试类 3.测 ...
- 002 Hello Spring Security
在前面已经搭建过环境框架,现在在demo模块下写一个简单的案例,让整个环境跑起来. 一:启动Demo项目 1.新建类 在这前,先建立包. 2.启动类程序 package com.cao; import ...
- fastAdmin进阶
基本知识流程一栏链接 bootstrapTable fastadmin系统配置(符内置规则): fastadmin默认的controller已实现的方法 一张图解析fastadmin的表格: fast ...
- Jenkins使用遇到的问题总结
学新技能最方便的就是在网上找教程了,我找到一个还不错的易百教程 Jenkins教程 别看教程写了一堆,其实真正用到的没多少.后面的自动化测试,服务器维护啥的,等用到的时候再深入研究吧 No1: 按照教 ...
- Python虚拟环境的安装和配置-virtualenv与windows下多个python版本共存
Python虚拟环境的安装和配置-virtualenv与windows下多个python版本共存 windows下多个python版本共存 https://www.python.org/downloa ...
- JDBC概述
什么是持久化(persistence):持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用.大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固 ...
- linux基础权限管理
1.linux系统中的文件类型:- 一般文件 存放数据d 目录文件 存放其他文件l 链接文件 类似于windows系统中的快捷方式b 区块设备文件 可以随时读取c 字符设备文件 需要顺序读 ...