NIO 源码分析(01) NIO 最简用法

Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)

Java NIO 主要由三个部分组成:Channel、Buffer 和 Selector。在分析源码前最好对 NIO 的基本用法和 Linux NIO 在一个基本的了解。

  1. NIO 入门
  2. Linux NIO

本文会提供一个 NIO 最简使用示例,之后的源码分析都会基于该示例及其扩展进行。

一、服务端

public class Server implements Runnable {
public static void main(String[] args) {
new Thread(new Server(8765)).start();
} // 1 多路复用器(管理所有的通道)
private Selector selector;
// 2 建立缓冲区
private ByteBuffer readBuf = ByteBuffer.allocate(1024);
private ByteBuffer writeBuf = ByteBuffer.allocate(1024); public Server(int port) {
try {
//1. 获取多路复用器
this.selector = Selector.open(); //2. 获取服务器端通道
ServerSocketChannel ssChannel = ServerSocketChannel.open(); //3. 切换成非阻塞模式
ssChannel.configureBlocking(false); //4. 绑定端口号
ssChannel.bind(new InetSocketAddress(port)); //5. 把服务器通道注册到多路复用器上,并且监听阻塞事件
ssChannel.register(this.selector, SelectionKey.OP_ACCEPT); System.out.println("Server start, port :" + port);
} catch (IOException e) {
e.printStackTrace();
}
} public void run() {
while (true) {
try {
// 1. 阻塞到至少有一个通道在你注册的事件上就绪,返回的 int 值表示有多少通道已经就绪
int wait = selector.select();
if (wait == 0) continue; //2. 遍历多路复用器上已经准备就绪的通道
Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next(); if (key.isValid()) {
if (key.isAcceptable()) {
// 3.1 如果为 accept 状态,就获取客户端通道并注册到selector上
this.accept(key);
} else if (key.isReadable()) {
// 3.2 如果为可读状态
this.read(key);
} else if (key.isWritable()) {
// 3.3 写数据
this.write(key);
}
} // 4. 注意必须在处理完通道时自己移除
iterator.remove();
}
} catch (IOException e) {
e.printStackTrace();
}
}
} private void write(SelectionKey key) {
//ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//ssc.register(this.seletor, SelectionKey.OP_WRITE);
} private void read(SelectionKey key) {
try {
//1. 清空缓冲区旧的数据
this.readBuf.clear(); //2. 获取之前注册的socket通道对象
SocketChannel sChannel = (SocketChannel) key.channel(); //3. 读数据
int len = sChannel.read(this.readBuf);
if (len == -1) {
key.channel().close();
key.cancel();
return;
} //5 有数据则进行读取 读取之前要flip()复位
this.readBuf.flip();
byte[] bytes = new byte[this.readBuf.remaining()];
this.readBuf.get(bytes); System.out.println("Server : " + new String(bytes).trim());
//6. 可以写回给客户端数据
} catch (IOException e) {
e.printStackTrace();
} } private void accept(SelectionKey key) {
try {
//1. 获取服务通道ServerSocketChannel
ServerSocketChannel ssChannel = (ServerSocketChannel) key.channel(); //2. 获取客户端通道SocketChannel
SocketChannel sChannel = ssChannel.accept(); //3. 客户端通道SocketChannel也要设置成非阻塞模式
sChannel.configureBlocking(false); //4 注册到多路复用器上,并设置读取标识
sChannel.register(this.selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
}

二、客户端

public static void main(String[] args) throws IOException {
SocketChannel sChannel = null;
try {
//1. 获取通道
sChannel = SocketChannel.open(
new InetSocketAddress("127.0.0.1", 8765)); //2. 切换成非阻塞模式
sChannel.configureBlocking(false); //3. 分配缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024); //4. 把从控制台读到数据发送给服务器端
Scanner scanner = new Scanner(System.in);
while (true) {
String str = scanner.next();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
buf.put((dateFormat.format(new Date()) + ":" + str).getBytes());
buf.flip();// 非阻塞模式下,write()方法在尚未写出任何内容时可能就返回了
while (buf.hasRemaining()) {
sChannel.write(buf);
}
buf.clear();
}
} finally {
sChannel.close();
}
}

ok,完成!!!


每天用心记录一点点。内容也许不重要,但习惯很重要!

NIO 源码分析(01) NIO 最简用法的更多相关文章

  1. NIO 源码分析(02-2) BIO 源码分析 Socket

    目录 一.BIO 最简使用姿势 二.connect 方法 2.1 Socket.connect 方法 2.2 AbstractPlainSocketImpl.connect 方法 2.3 DualSt ...

  2. NIO 源码分析(02-1) BIO 源码分析

    目录 一.BIO 最简使用姿势 二.ServerSocket 源码分析 2.1 相关类图 2.2 主要属性 2.3 构造函数 2.4 bind 方法 2.5 accept 方法 2.6 总结 NIO ...

  3. NIO 源码分析(05) Channel 源码分析

    目录 一.Channel 类图 二.begin 和 close 是什么 2.1 AbstractInterruptibleChannel 中的 begin 和 close 2.2 Selector 中 ...

  4. NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制

    目录 一.SelectorProvider SPI 二.SelectorProvider 加载过程 2.1 SelectorProvider 加载 2.2 Windows 下 DefaultSelec ...

  5. NIO 源码分析(03) 从 BIO 到 NIO

    目录 一.NIO 三大组件 Channels.Buffers.Selectors 1.1 Channel 和 Buffer 1.2 Selector 1.3 Linux IO 和 NIO 编程的区别 ...

  6. 一个由正则表达式引发的血案 vs2017使用rdlc实现批量打印 vs2017使用rdlc [asp.net core 源码分析] 01 - Session SignalR sql for xml path用法 MemCahe C# 操作Excel图形——绘制、读取、隐藏、删除图形 IOC,DIP,DI,IoC容器

    1. 血案由来 近期我在为Lazada卖家中心做一个自助注册的项目,其中的shop name校验规则较为复杂,要求:1. 英文字母大小写2. 数字3. 越南文4. 一些特殊字符,如“&”,“- ...

  7. NIO源码分析:SelectionKey

    SelectionKey SelectionKey,选择键,在每次通道注册到选择器上时都会创建一个SelectionKey储存在该选择器上,该SelectionKey保存了注册的通道.注册的选择器.通 ...

  8. [asp.net core 源码分析] 01 - Session

    1.Session文档介绍 毋庸置疑学习.Net core最好的方法之一就是学习微软.Net core的官方文档:https://docs.microsoft.com/zh-cn/aspnet/cor ...

  9. dubbo源码分析01:SPI机制

    一.什么是SPI SPI全称为Service Provider Interface,是一种服务发现机制,其本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件.这样可以在运行时,动态为 ...

随机推荐

  1. Linux基础-命令概述

    概述 很多人可能在电视或电影中看到过类似的场景,黑客面对一个黑色的屏幕,上面飘着密密麻麻的字符,梆梆一顿敲,就完成了窃取资料的任务,是不是很帅!我们作为一个开发者, 即使不为了成为上述的人, 也需要会 ...

  2. Config JAVA evironment for LoadRunner

    1. Install jdk 2. Set system variables eg. JAVA_HOME = C:\Program Files (x86)\Java\jdk1.6.0_43 class ...

  3. 判断是否英文字母或数字的C#正则表达式

    private int IsDigitOrNumber(string str) { if(System.Text.RegularExpressions.Regex.IsMatch(str,@" ...

  4. Git操作思维导图

    转自:https://blog.csdn.net/mynameishuangshuai/article/details/51657324

  5. jmeter 不同线程组之间传递变量2

    方法1  通过变量传递参数: 第一个脚本: HTTP Request_新建出差申请单_登录,关联出参数token.companyId.userId.userName 1.添加后置处理器:BeanShe ...

  6. python调用tushare获取沪深股通十大成交股

    接口:hsgt_top10 描述:获取沪股通.深股通每日前十大成交详细数据 注:tushare库下载和初始化教程,请查阅我之前的文章 输入参数 名称      |      类型      |    ...

  7. C++之循环体内变量

    今天做PAT题目时候看人家解答: #include <cstdio> #include <set> using namespace std; int main() { int ...

  8. 自定义servlet重写doGet或者doPost方法时,405 method not allowed

    自定义servlet public class TestServlet extends HttpServlet { @Override protected void doGet(HttpServlet ...

  9. Activiti学习笔记3 — 流程定义

    一.创建流程引擎对象 private ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 二.发布一个流程 ...

  10. 【Luogu】【关卡2-12】递推与递归二分(2017年10月)

    任务说明:递推,层层递进,由基础推向顶层.二分不仅可以用来查找数据,还可以确定最合适的值. P1192 台阶问题 有N级的台阶,你一开始在底部,每次可以向上迈最多K级台阶(最少1级),问到达第N级台阶 ...