Java NIO使用及原理分析 (四)
在上一篇文章中介绍了关于缓冲区的一些细节内容,现在终于可以进入NIO中最有意思的部分非阻塞I/O。通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据。同样,写入调用将会阻塞直至数据能够写入。传统的Server/Client模式会基于TPR(Thread per Request),服务器会为每个客户端请求建立一个线程,由该线程单独负责处理一个客户请求。这种模式带来的一个问题就是线程数量的剧增,大量的线程会增大服务器的开销。大多数的实现为了避免这个问题,都采用了线程池模型,并设置线程池线程的最大数量,这由带来了新的问题,如果线程池中有200个线程,而有200个用户都在进行大文件下载,会导致第201个用户的请求无法及时处理,即便第201个用户只想请求一个几KB大小的页面。传统的
Server/Client模式如下图所示:

NIO中非阻塞I/O采用了基于Reactor模式的工作方式,I/O调用不会被阻塞,相反是注册感兴趣的特定I/O事件,如可读数据到达,新的套接字连接等等,在发生特定事件时,系统再通知我们。NIO中实现非阻塞I/O的核心对象就是Selector,Selector就是注册各种I/O事件地 方,而且当那些事件发生时,就是这个对象告诉我们所发生的事件,如下图所示:

从图中可以看出,当有读或写等任何注册的事件发生时,可以从Selector中获得相应的SelectionKey,同时从 SelectionKey中可以找到发生的事件和该事件所发生的具体的SelectableChannel,以获得客户端发送过来的数据。关于 SelectableChannel的可以参考Java
NIO使用及原理分析(一)
使用NIO中非阻塞I/O编写服务器处理程序,大体上可以分为下面三个步骤:
1. 向Selector对象注册感兴趣的事件
2. 从Selector中获取感兴趣的事件
3. 根据不同的事件进行相应的处理
接下来我们用一个简单的示例来说明整个过程。首先是向Selector对象注册感兴趣的事件:
- /*
- * 注册事件
- * */
- protected Selector getSelector() throws IOException {
- // 创建Selector对象
- Selector sel = Selector.open();
- // 创建可选择通道,并配置为非阻塞模式
- ServerSocketChannel server = ServerSocketChannel.open();
- server.configureBlocking(false);
- // 绑定通道到指定端口
- ServerSocket socket = server.socket();
- InetSocketAddress address = new InetSocketAddress(port);
- socket.bind(address);
- // 向Selector中注册感兴趣的事件
- server.register(sel, SelectionKey.OP_ACCEPT);
- return sel;
- }
创建了ServerSocketChannel对象,并调用configureBlocking()方法,配置为非阻塞模式,接下来的三行代码把该通道绑定到指定端口,最后向Selector中注册事件,此处指定的是参数是OP_ACCEPT,即指定我们想要监听accept事件,也就是新的连接发 生时所产生的事件,对于ServerSocketChannel通道来说,我们唯一可以指定的参数就是OP_ACCEPT。
从Selector中获取感兴趣的事件,即开始监听,进入内部循环:
- /*
- * 开始监听
- * */
- public void listen() {
- System.out.println("listen on " + port);
- try {
- while(true) {
- // 该调用会阻塞,直到至少有一个事件发生
- selector.select();
- Set<SelectionKey> keys = selector.selectedKeys();
- Iterator<SelectionKey> iter = keys.iterator();
- while (iter.hasNext()) {
- SelectionKey key = (SelectionKey) iter.next();
- iter.remove();
- process(key);
- }
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
在非阻塞I/O中,内部循环模式基本都是遵循这种方式。首先调用select()方法,该方法会阻塞,直到至少有一个事件发生,然后再使用selectedKeys()方法获取发生事件的SelectionKey,再使用迭代器进行循环。
最后一步就是根据不同的事件,编写相应的处理代码:
- /*
- * 根据不同的事件做处理
- * */
- protected void process(SelectionKey key) throws IOException{
- // 接收请求
- if (key.isAcceptable()) {
- ServerSocketChannel server = (ServerSocketChannel) key.channel();
- SocketChannel channel = server.accept();
- channel.configureBlocking(false);
- channel.register(selector, SelectionKey.OP_READ);
- }
- // 读信息
- else if (key.isReadable()) {
- SocketChannel channel = (SocketChannel) key.channel();
- int count = channel.read(buffer);
- if (count > 0) {
- buffer.flip();
- CharBuffer charBuffer = decoder.decode(buffer);
- name = charBuffer.toString();
- SelectionKey sKey = channel.register(selector, SelectionKey.OP_WRITE);
- sKey.attach(name);
- } else {
- channel.close();
- }
- buffer.clear();
- }
- // 写事件
- else if (key.isWritable()) {
- SocketChannel channel = (SocketChannel) key.channel();
- String name = (String) key.attachment();
- ByteBuffer block = encoder.encode(CharBuffer.wrap("Hello " + name));
- if(block != null)
- {
- channel.write(block);
- }
- else
- {
- channel.close();
- }
- }
- }
此处分别判断是接受请求、读数据还是写事件,分别作不同的处理。
Java NIO使用及原理分析 (四)的更多相关文章
- Java NIO使用及原理分析 (四)(转)
在上一篇文章中介绍了关于缓冲区的一些细节内容,现在终于可以进入NIO中最有意思的部分非阻塞I/O.通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据.同样,写入调用将会阻塞直至 ...
- Java NIO使用及原理分析(二)
在第一篇中,我们介绍了NIO中的两个核心对象:缓冲区和通道,在谈到缓冲区时,我们说缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如 ...
- Java NIO使用及原理分析(二)(转)
在第一篇中,我们介绍了NIO中的两个核心对象:缓冲区和通道,在谈到缓冲区时,我们说缓冲区对象本质上是一个数组,但它其实是一个特殊的数组,缓冲区对象内置了一些机制,能够跟踪和记录缓冲区的状态变化情况,如 ...
- Java NIO使用及原理分析(1-4)(转)
转载的原文章也找不到!从以下博客中找到http://blog.csdn.net/wuxianglong/article/details/6604817 转载自:李会军•宁静致远 最近由于工作关系要做一 ...
- Java NIO使用及原理分析 (一)(转)
最近由于工作关系要做一些Java方面的开发,其中最重要的一块就是Java NIO(New I/O),尽管很早以前了解过一些,但并没有认真去看过它的实现原理,也没有机会在工作中使用,这次也好重新研究一下 ...
- Java NIO使用及原理分析(三)
在上一篇文章中介绍了缓冲区内部对于状态变化的跟踪机制,而对于NIO中缓冲区来说,还有很多的内容值的学习,如缓冲区的分片与数据共享,只读缓冲区等.在本文中我们来看一下缓冲区一些更细节的内容. 缓冲区的分 ...
- Java NIO使用及原理分析(三)(转)
在上一篇文章中介绍了缓冲区内部对于状态变化的跟踪机制,而对于NIO中缓冲区来说,还有很多的内容值的学习,如缓冲区的分片与数据共享,只读缓冲区等.在本文中我们来看一下缓冲区一些更细节的内容. 缓冲区的分 ...
- Java NIO使用及原理分析 (一)
http://blog.csdn.net/wuxianglong/article/details/6604817
- Java NIO wakeup实现原理
本文转载自Java NIO wakeup实现原理 导语 最近在阅读netty源码时,很好奇Java NIO中Selector的wakeup()方法是如何唤醒selector的,于是决定深扒一下wake ...
随机推荐
- C++相关资源
http://www.cnblogs.com/xi52qian/p/4186983.html语言ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ...
- Swift(二,元组,可选类型,类型转化)
一,首先,元组是Swift中特有的,OC中没有元组相关类型,具体怎么用,看下面的例子吧 //1.使用元组来定义一组数据 let infoTuple = (,1.8) let nameTuple = i ...
- keilkill.bat
一.批处理文件 批处理文件是无格式的文本文件,它包含一条或多条命令.它的文件扩展名为 .bat 或 .cmd.在命令提示下键入批处理文件的名称,或者双击该批处理文件,系统就会调用Cmd.exe按照该文 ...
- 4071: [Apio2015]巴邻旁之桥
Description 一条东西走向的穆西河将巴邻旁市一分为二,分割成了区域 A 和区域 B. 每一块区域沿着河岸都建了恰好 1000000001 栋的建筑,每条岸边的建筑都从 0 编号到 10000 ...
- 基于h5+ajax实现的手机定位
因朋友需要,之前看了下关于h5的手机定位,目前写了个demo在这里贴出来,感兴趣的朋友可以看一下. 目前的版本只是demo,仍有几个问题需要完善一下,问题如下: 1,如何将经纬度等数据发送到被定位人看 ...
- [原博客] POI系列(2)
正规.严谨.精妙. -POI bzoj 1098 : [POI2007]办公楼biu 如果把互相有手机号的建边得到一个无向图,那么这个图的补图的连通分量个数就是答案了.因为互相没手机号的必然在同一个连 ...
- DEEP LEARNING IS THE FUTURE: Q&A WITH NAVEEN RAO OF NERVANA SYSTEMS
DEEP LEARNING IS THE FUTURE: Q&A WITH NAVEEN RAO OF NERVANA SYSTEMS CME Group was one of several ...
- 【Hihocoder 1167】 高等理论计算机科学 (树链的交,线段树或树状数组维护区间和)
[题意] 时间限制:20000ms 单点时限:1000ms 内存限制:256MB 描述 少女幽香这几天正在学习高等理论计算机科学,然而她什么也没有学会,非常痛苦.所以她出去晃了一晃,做起了一些没什么意 ...
- [转贴]WebService的简单实现 C++
WebService的简单实现 一.socket主机创建和使用过程 1.socket()//创建套接字 2.Setsockopt()//将套接字属性设置为允许和特定地点绑定 3.Bind()//将套接 ...
- 在TMemo上画一条线
var C:TControlCanvas; begin C := TControlCanvas.Create; C.Pen.Color := clRed; C.Pen.Width := ; C.Con ...