No-Block
和Block
IO
的区别:
一个典型的网络通讯步骤为: open
(新建socket Chanel
)-->
connect(
尝试建立连接)
-->
accept(
连接被接受)
-->
read(
读取请求)
send
(输出结果)-->
close(
连接关闭)
。
对于一个No-Block
的网络IO
,上面的每一步都是会马上返回的,当然返回的结果可能为null
,可能不为null
,这个要看下上文(context
)决定。一般情况下,我们都是需要不为null
的结果,这个就需要我们在适当的时机,执行适当的步骤,这样就会得到我们想要的结果。何为适当的时机?这个下面会讲。
对于一个block
的网络IO
,上面的每一步执行的时候,如果没到适当的时机,当前线程就会被block
住,直到适当的时机,返回给你确定的结果。
当然对与No-Block
或者Block IO
,上面的每一步都有可能会抛出IOException
异常的。
NIO
编程接触的几个关键概念:
Buffer
:是一块连续的内存块,是 NIO
数据读或写的中转地。Buffer
这篇blog
暂时略过不讲。
Chanel
:数据的源头或者数据的目的地,用于向 buffer
提供数据或者读取 buffer
数据 ,异步 I/O
支持。
注意chanel
有2
类,一种叫SocketChanel,
一种叫ServerSocketChanel
,看名字我们就知道,一类是普通的socket chanel
,client
端和服务器端都用的,一类是专门用在server
端的。当然这个界限也不是绝对的,互为client
和server
的情况也是存在的。
Selector
:
chanel
事件的侦听者,
它能检测一个或多个通道
(channel)
上的事件,并将事件分发出去。使用一个
select
线程就能监听多个通道上的事件,并基于事件驱动触发相应的响应。
SelectionKey
:
chanel
上发生的事件,
包含了事件的状态信息和时间以及对应的
chanel
。
Chanel
的状态:
可连(
Connectable
):当一个
Chanel
完成
socket
连接操作已完成或者已失败放弃时
能连(
Acceptable
):当一个
Chanel
已经准备好接受一个新的
socket
连接时
可读(
Readable
):当一个
Chanel
能被读时
可写(
Writable
):当一个
Chanel
能被写时
结合对照上面的网络通讯步骤我们可以有以下推导出的结论:
当一个
Server Chanel
是
Connectable
时,
client
端尝试
connect
才会成功。
当一个
Server
Chanel
是
Acceptable
时,
client
的连接请求被真正受理,一个新的
chanel
会被生成,并且记录了
localAdrress
和
remoteAddress.
为进一步读写做准备。
当一个
Chanel
是
Readable
时,我们从这个
Chanel
中读取数据才会成功。
当一个
Chanel
是
Writable
时,我们往这个
Chanel
中写数据才会成功。
记住一点,对于一个
No-Block
的
Chanel
来说,上面
4
个操作都会马上返回或者抛出
IOException
,但是是不是成功就难说了,前面就说了,我们在一个
Chanel
做操作的时候,我们要密切关注
Chanel
的当前状态。只有在知道
Chanel
的当前状态时,我们才能在这个
Chanel
上做最适当的操作。
聪明的你可能马上就会想到,要是你操作的
Chanel
的状态的转换信息能被你抓取,这些问题就迎刃而解了。对啦,
NIO
就是这样设计的。一个
Chanel
可以注册一个
Selector
(就像一个事件侦听器),而且你还要告知你想要要侦听的状态。用一段代码来说明下:
selector =
SelectorProvider.provider().openSelector();
serverChannel1 =
ServerSocketChannel.open();
serverChannel1.configureBlocking(false);
InetSocketAddress isa = new
InetSocketAddress("localhost", 9999);
serverChannel1.socket().bind(isa);
serverChannel1.register(selector,
SelectionKey.OP_ACCEPT);
这段代码的意思就是我们打开了一个
ServerChanel
,侦听本机的
9999
端口,并且新建了一个
Selector,
然后这个
ServerChanel
注册了这个
Selector
,并且指定了它感兴趣的状态类型是
OP_ACCEPT.
这样有什么效果呢?
注意红色那句,这句意思是selector要求serverChannel1状态为acceptable的时候把这个消息告诉selector.
效果就是:
当这个
ServerChanel
状态为
Acceptable
时,
Selector
就会收到一个消息,这个消息当然就是一个
SelectionKey
对象。调用
Selector
的
selectedKeys
()方法,我们就能得到所有
Chanel
发送过来的消息。
因为
SelectionKey
包含
事件的状态,时间以及对应的
Chanel
,很自然的,我们遍历这个
Set<SelectionKey>,
根据
SelectionKey
的状态,就能在相应的
Chanel
做正确的操作。比如,能读的时候我们就读,能写的时候我们就写。
最后讲讲
Server
端和
Client
编程的一般步骤:
对于
Client
来一般是这样的:
InetSocketAddress isa = new InetSocketAddress(host,
port);
SocketChannel sc = null;
sc = SocketChannel.open();
sc.connect(isa);
sc.write(data);
…
Sc.read(buff);
构造一个
InetSocketAddress
对象
-->
open
-->
connect
-->
write
-->
read
注意这里用的不是
No-Block
的方式,因为
client
如果没有得到
server
端的正确回应的话就采取下一步操作无疑是没有意义的。
Server
端:
selector
= SelectorProvider.provider
().openSelector();
serverChannel
= ServerSocketChannel.open
();
serverChannel
.configureBlocking(
false
);
InetSocketAddress isa =
new
InetSocketAddress(
"localhost"
,
9999
);
serverChannel
.socket().bind(isa);
serverChannel
.register(
selector
, SelectionKey.
OP_ACCEPT
);
构造一个
Selector
-->
打开一个
serverSocketChanel
-->
设定
serverSocketChanel
为
no-block-->
bind serverSocketChanel
到一个
host
和
port
-->
register Selector
并告知感兴趣的状态类型转换。
在
SelectionKey
Set
上遍历操作:
while (true) {
selector.select();
Iterator selectedKeys =
this.selector.selectedKeys().iterator();
while
(selectedKeys.hasNext()) {
SelectionKey key = (SelectionKey)
selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
accept(key);
} else if (key.isReadable()) {
read(key);
} else if (key.isWritable()) {
write(key);
}
}
}
在这个循环里面我们会根据
SelectionKey
的状态,采取不同的操作的。当连接被
accepted
时,
一个新的
chanel
会被生成,并且记录了
localAdrress
和
remoteAddress.
为进一步读写做准备。
accept
函数如下:
public void accept(SelectionKey key) throws
IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel)
key.channel();
SocketChanel socketChannel1 = serverSocketChannel.accept();
socketChannel1.configureBlocking(false);
socketChannel1.register(selector, SelectionKey.OP_READ);
}
这里新的
Chanel
被构建,最后同样会注册到
selector
,
同时要求当这个
Chanel
为
Readable
时,一个
SelectionKey
被放入到
Selector
中。这样上面循环会用
read(key)
来处理这个
SelectionKey.
分享到:
相关推荐
我研究并实现的Java Nio selector例子。java侧起server(NioUdpServer1.java),基于Java Nio的selector 阻塞等候,一个android app(NioUdpClient1文件夹)和一个java程序(UI.java)作为两个client分别向该server...
基于JavaNIO的非阻塞通信的研究与实现
NULL 博文链接:https://zfms.iteye.com/blog/1535594
基于Java NIO的高校心理咨询系统的研究与设计.pdf
#资源达人分享计划# java nio从入门到精通的代码详解 主要结合博文进行学习 详情可配合我的博客进行学习 https://blog.csdn.net/weixin_47872288/article/details/120342049
java课程设计大作业,java、算法练手项目,适合初学java、数据结构的同学拿来学习研究,基于java、GUI开发的小游戏,程序都经过测试,可以直接运行,资源含程序运行所需的源码、资源文件等全部数据,有需要的可放心...
重新写的Java.nio的socket异步通讯,包含客户端及服务端完整版, 注:解析信息CLASS及进制转换CLASS需要自己去写,项目直接导入,需要自己写一些解析及转换类,这样你才能读懂SOCKET的异步,否则光拿代码没用 ...
近代: java.nio java.niojava.niojava.niojava.niojava.nio 其他: MinaMinaMinaMina,GrizzlyGrizzlyGrizzly GrizzlyGrizzly 为什么不是 为什么不是 MinaMina Mina? 1、都是 Trustin LeeTrustin LeeTrustin Lee ...
分析了应用传统阻塞型网络I/O 在进行服务器程序设计时的不足, 在此基础上研究了非阻塞型网络I/O (NIO)的特性以及工作机制,给出了应用NIO 设计高性能并发型服务器程序核心代码,以实现服务器与客户端无阻 塞的、...
1. 培训内容设计:包括Java编程语言基础、面向对象编程、集合框架、多线程编程、IO与NIO、网络编程、数据库编程、Spring框架、设计模式等诸多模块,需要根据学员的学科背景和研究方向进行合理的内容设计。...
905.4-2014协议,是交通运输部公路科学研究院起草定制的一个协议标准,它也是基于TCP之上的一个应用层传输协议。 第2章,介绍在Socket编程过程中一些基础知识,让大家建立起对这块知识内容的一个整体轮廓; 第3章,...
JoSQL(SQLforJavaObjects)为Java开发者提供运用SQL语句来操作Java对象集的能力.利用JoSQL可以像操作数据库中的数据一样对任何Java对象集进行查询,排序,分组。 搜索自动提示 Autotips AutoTips是为解决应用系统对于...
Netty权威指南 第2版 带书签目录 完整版,适合java NIO的研究学习
论文首先论述了系统的研究背景和目标,并对所用到开发工具与关键技术做了简单的介绍。...使用Java语言结合xml技术提高了系统的可靠性和可以移植性,应用NIO技术很大程度上提高系统的网络吞吐量和数据处理能力。
nio-study:尼奥研究
本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...
java版联机五子棋,包含服务器端及用户端,服务器端采用nio实现。包含游戏大厅,可查看在线玩家及玩家数据,支持大厅广播及私聊;支持多人同时对战,支持悔棋、认输;支持多人观战;支持查看历史对战记录,支持添加...
研究了由时变引力场结合连续粒子产生驱动的宇宙演化。 在广义相对论的背景下,我们考虑具有两种物质流体的空间平坦,均匀且各向同性的宇宙。 一种流体被赋予重力诱导的“绝热”粒子生成,而第二种流体仅满足能量守恒...