NIO 源码分析(01) NIO 最简用法
NIO 源码分析(01) NIO 最简用法
Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)
Java NIO 主要由三个部分组成:Channel、Buffer 和 Selector。在分析源码前最好对 NIO 的基本用法和 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 最简用法的更多相关文章
- NIO 源码分析(02-2) BIO 源码分析 Socket
目录 一.BIO 最简使用姿势 二.connect 方法 2.1 Socket.connect 方法 2.2 AbstractPlainSocketImpl.connect 方法 2.3 DualSt ...
- NIO 源码分析(02-1) BIO 源码分析
目录 一.BIO 最简使用姿势 二.ServerSocket 源码分析 2.1 相关类图 2.2 主要属性 2.3 构造函数 2.4 bind 方法 2.5 accept 方法 2.6 总结 NIO ...
- NIO 源码分析(05) Channel 源码分析
目录 一.Channel 类图 二.begin 和 close 是什么 2.1 AbstractInterruptibleChannel 中的 begin 和 close 2.2 Selector 中 ...
- NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制
目录 一.SelectorProvider SPI 二.SelectorProvider 加载过程 2.1 SelectorProvider 加载 2.2 Windows 下 DefaultSelec ...
- NIO 源码分析(03) 从 BIO 到 NIO
目录 一.NIO 三大组件 Channels.Buffers.Selectors 1.1 Channel 和 Buffer 1.2 Selector 1.3 Linux IO 和 NIO 编程的区别 ...
- 一个由正则表达式引发的血案 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. 一些特殊字符,如“&”,“- ...
- NIO源码分析:SelectionKey
SelectionKey SelectionKey,选择键,在每次通道注册到选择器上时都会创建一个SelectionKey储存在该选择器上,该SelectionKey保存了注册的通道.注册的选择器.通 ...
- [asp.net core 源码分析] 01 - Session
1.Session文档介绍 毋庸置疑学习.Net core最好的方法之一就是学习微软.Net core的官方文档:https://docs.microsoft.com/zh-cn/aspnet/cor ...
- dubbo源码分析01:SPI机制
一.什么是SPI SPI全称为Service Provider Interface,是一种服务发现机制,其本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件.这样可以在运行时,动态为 ...
随机推荐
- asp.net 随笔
20190625 网站发布后,添加文件,必须重启iis的应用程序池和网站才能使用添加的文件.
- 极致CMS建站系统后台GETSHELL
起因 正在学习代码审计 看到有人提交了一个注入https://www.cnvd.org.cn/flaw/show/CNVD-2019-42775 想试试看还有没有别的漏洞 受影响版本 v1.6.3 - ...
- python作业/练习/实战:3、实现商品管理的一个程序
作业要求 实现一个商品管理的一个程序,运行程序有三个选项,输入1添加商品:输入2删除商品:输入3 查看商品信息1.添加商品: 商品名称:xx 商品如果已经存在,提示商品已存在 商品价格:xx数量只能为 ...
- mac、windows系统charles安装破解
一.下载 官网下载适合自己电脑的最新版本 下载地址:https://www.charlesproxy.com/latest-release/download.do 二.破解 破解地址:https:// ...
- 在webpack4 中利用Babel 7取消严格模式方法
报错信息: Uncaught TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on stri ...
- OpenGL中的空间变换
OpenGL中的空间变换 在使用OpenGL的三维虚拟程序中.当我们指定了模型的顶点之后.在屏幕上显示它们之前,一共会发生3种类型的变换:视图变换.模型变换.投影变换. ...
- Lock中使用Condition实现等待通知
Condition类有很好的灵活性,可以实现多路通知功能,一个Lock对象中可以创建多个Condition对象实例,线程对象可以注册在指定的Condition中,进而有选择的进行线程通知,在调度线程上 ...
- miaosha
1:跨域请求配置 后端Controller 添加注解 @CrossOrigin(origins = {"*"},allowCredentials = "true" ...
- Android Service完全解析(上)
转载:http://blog.csdn.net/guolin_blog/article/details/11952435 相信大多数朋友对Service这个名词都不会陌生,没错,一个老练的Androi ...
- mysql 判断指定条件数据存不存在,不存在则插入
折腾了半天终于把这个给折腾顺了,但是后来发现用不了竟然...悲剧啊,但是还是要记录下加深记忆 insert into table1 (field1, field2,field3) select ?fi ...