ytes("UTF-8"));
48 byteBuffer.flip();
49 clientChannel.write(byteBuffer);
50 //clientChannel.register(key.selector(),SelectionKey.OP_READ);
51 clientChannel.close();
52 }
53
54 public void doRead(SelectionKey key) throws IOException {
55 SocketChannel clientChannel = (SocketChannel) key.channel();
56 clientChannel.read(byteBuffer);
57 byte[] data = byteBuffer.array();
58 String msg = new String(data).trim();
59 System.out.println("服务端发送消息:"+msg);
60 clientChannel.close();
61 key.selector().close();
62 }
63
64 public static void main(String[] args) throws IOException {
65 MyNioClient myNioClient = new MyNioClient();
66 myNioClient.initClient();
67 }
68 }
View Code
在早期的JDK1.4和1.5 update10版本之前,Selector基于select/poll模型实现,是基于IO复用技术的非阻塞IO,不是异步IO。在JDK1.5 update10和linux core2.6以上版本,sun优化了Selctor的实现,底层使用epoll替换了select/poll。另据说Buffer指向的并非堆内内存,NIO使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆的 DirectByteBuffer 对象作为这块内存的引用进行操作,避免了在 Java 堆和 Native 堆中来回复制数据。
NIO的实现解析可参看:深入浅出NIO Socket实现机制
Reactor模式
NIO为实现Reactor模式提供了基础,上面的NIO图示其实就是Reactor模式的雏形,只是Reactor以OO的方式抽象出了几个概念,使得职责划分更加明确。
- Reactor:Reactor是IO事件的派发者,对应NIO的Selector;
- Acceptor:Acceptor接受client连接,建立对应client的Handler,并向Reactor注册此Handler,对应NIO中注册Channel和事件触发时的判断分支(上述NIO服务端示例代码的38-46行);
- Handler:IO处理类,对应NIO中Channel[使用socket]操作Buffer的过程。
基于上述三个角色画出Reactor模式图如下:
如此,Reactor模式便非常清晰地展现在我们眼前。那么业务线程如何与Reactor交互呢?由前文所知,数据存取于Buffer,具体操作由Handler负责。socket.read()将数据读入Buffer,需要一种机制将Buffer引用推送给业务线程;同样,业务线程返回的数据需要写入Buffer,按Reactor模式,写入后还需要注册write事件,socket可写后write()。如果直接调用的话,至少Handler和业务代码会耦合在一起,常见的解耦方式是定义接口,或使用消息中间件。
其它
话说回来,由于相对短暂的历史以及相对封闭的环境,.Net社区缺少很多概念的演化、探究和讨论,这也导致了.Neter们这些概念的缺失。虽然从语言层面上来说,C#和Java大同小异,前者甚至一定程度的有语法上的便利,然而只有认识到了其背后的思想和模式,才能真正用好这门语言,这就是.Neter需要了解Java及其历史的原因,毕竟.Net一开始就是参照着Java来的。
比如.Net里的堆栈概念,就算一些经典书籍都没有非常深入的说明,而Java方面的资料就很多了,参看深入理解JVM—JVM内存模型
其它参考资料:
NIO浅析
深入理解Java NIO
网络通信socket连接数上限
转载请注明本文出处:https://www.cnblogs.com/newton/p/9776821.html