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,是一种服务发现机制,其本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件.这样可以在运行时,动态为 ...
随机推荐
- spss乱码问题解决
spss乱码问题解决 方法1:网友kuangsir6提供 选择字体为:DFKai-SB 格式(我并没有找到这个格式) 方法是 SPSS(PASW)---Edit---Options---Viewer- ...
- 前端学习笔记——HTML
1.前端的三把利器 HTML:我们把它比喻成赤裸的一个人 CSS:华丽的衣服 JS/JavaScript:赋予这个人的行为,也就是动起来 2.什么是HTML? HTML 是用来描述网页的一种语言.ht ...
- poi 处理空单元格
http://poi.apache.org/spreadsheet/quick-guide.html 用google翻译 找到
- 第六周实验总结&学习总结
一.实验目的 (1)掌握类的继承方法 (2)变量的继承和覆盖,方法的继承,重载和覆盖实现 二.实验内容 源代码 package java2; import java.util.Scanner; cla ...
- React-Native 指定模拟器RUN-IOS
react-native run-ios --simulator "iPhone 7”
- Python之字典推导式
推导式是个绝妙的东西,列表推导式一出,map.filter等函数黯然失色,自 Python2.7以后的版本,此特性扩展到了字典和集合身上,构建字典对象无需调用 dict 方法. bad numbers ...
- 案例:forEach和some区别
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- termios, tcgetattr, tcsetattr, tcsendbreak, tcdrain, tcflush, tcflow, cfmakeraw, cfgetospeed, cfgetispeed, cfsetispeed, cfsetospeed - 获取和设置终端属性,行控制,获取和设置波特率
SYNOPSIS 总览 #include <termios.h> #include <unistd.h> int tcgetattr(int fd, struct termio ...
- vue在element-ui的dialog弹出框中加入百度地图
参考:https://blog.csdn.net/u012724595/article/details/82703579 <!-- gps弹窗 --> <el-dialog v-di ...
- mysql5.7 基于gtid的主从复制
基本环境 版本 5.7.14 主库ip:192.168.1.100 port:3306 从库ip:102.168.1.101 port:3306 搭建注意事项 主库配置 gtid-mode=on en ...