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,是一种服务发现机制,其本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件.这样可以在运行时,动态为 ...
随机推荐
- C. Ancient Berland Circus(三点确定最小多边形)
题目链接:https://codeforces.com/problemset/problem/1/C 题意:对于一个正多边形,只给出了其中三点的坐标,求这个多边形可能的最小面积,给出的三个点一定能够组 ...
- 【react】---redux-actions的基本使用---【巷子】
一.安装 cnpm install --save redux-actions 二.为什么使用 redux-actions reducer使用switch case语句进行action类型判断,当act ...
- Centos7安装 Hadoop(单节点)
1.Hadoop简介 Hadoop是一个由Apache基金会所开发的开源分布式系统基础框架,使用Java开发,是处理大规模数据的软件平台. Hadoop可以从单一节点扩展到上千节点.用户可以在不了解分 ...
- composer 配置镜像
阿里云镜像:composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ 腾讯云镜像:composer ...
- markdown_TestOne
这个是我写的一个markdown尝试 1.2 dafsdfeasdfaefasdfase afsdfasdfefasdfeadfasdfe
- SQL中to_char方法的应用
1.取得当前日期是本月的第几周 SQL> select to_char(sysdate,'YYYYMMDD W HH24:MI:SS') from dual; ----------------- ...
- 循序渐进学.Net Core Web Api开发系列【13】:中间件(Middleware)【有源码】
原文:循序渐进学.Net Core Web Api开发系列[13]:中间件(Middleware) 系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:ht ...
- Python之实现一个优先级队列
问题 怎样实现一个按优先级排序的队列? 并且在这个队列上面每次 pop 操作总是返回优先级最高的那个元素 解决方案 下面的类利用 heapq 模块实现了一个简单的优先级队列: import heapq ...
- spring+springmvc+mybatis框架搭建
一.开发前准备 1)ecplise4.11.0 百度网盘:https://pan.baidu.com/s/1wO9_I52lp0mYNeNTdnj80w 提取码:booa 2)jdk1.6.0_45 ...
- github for windows 简单的客户端托管代码
1)创建github账户 登录https://github.com,只需用户名.注册邮箱和登录密码便能注册一个属于自己的github(之后需要到注册邮箱中进行确认,非常喜欢这种注册方式,简单而且安全) ...