[转帖]NIO 实现非阻塞通信的原理_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 4332 | 回复: 0   主题: [转帖]NIO 实现非阻塞通信的原理        下一篇 
ab19890824
注册用户
等级:少尉
经验:351
发帖:92
精华:0
注册:2011-11-21
状态:离线
发送短消息息给ab19890824 加好友    发送短消息息给ab19890824 发消息
发表于: IP:您无权察看 2011-11-27 22:57:58 | [全部帖] [楼主帖] 楼主

说NIO的非阻塞原理之前,我们需要先说一下传统的io。传统的IO是按字节传输的,即每次传输一个字节。为了提高数据传输效率,引进了带缓冲区得输入输出模式,这样每次就可以传输大量的字节数,但是,会导致在读(写)缓冲区没有满的情况下,程序会一直等待,直到满或者关闭流才能读取(写入)。这样导致程序阻塞,降低了程序的执行效率。

如今的NIO的实现并非如此,他的传输是块传输,即每次传输一个块,一个块中可以包含大量的字节数,这样可以有效的提高传输速率而不会导致程序阻塞。如果你懂得设计模式,他的实现原理跟Observer模式类似,即观察者模式。我给你贴一段程序代码,你就能很好的理解。

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.net.*;
import java.util.*;
public class NBTest {
      public NBTest()
      {
      }
      public void startServer() throws Exception
      {
            int channels = 0;
            int nKeys = 0;
            int currentSelector = 0;
            //使用Selector
            Selector selector = Selector.open();
            //建立Channel 并绑定到9000端口
            ServerSocketChannel ssc = ServerSocketChannel.open();
            InetSocketAddress address = new InetSocketAddress(InetAddress.getLocalHost(),9000);
            ssc.socket().bind(address);
            //使设定non-blocking的方式。
            ssc.configureBlocking(false);
            //向Selector注册Channel及我们有兴趣的事件
            SelectionKey s = ssc.register(selector, SelectionKey.OP_ACCEPT);
            printKeyInfo(s);
            while(true) //不断的轮询
            {
                  debug("NBTest: Starting select");
                  //Selector通过select方法通知我们我们感兴趣的事件发生了。
                  nKeys = selector.select();
                  //如果有我们注册的事情发生了,它的传回值就会大于0
                  if(nKeys > 0)
                  {
                        debug("NBTest: Number of keys after select operation: " +nKeys);
                        //Selector传回一组SelectionKeys
                        //我们从这些key中的channel()方法中取得我们刚刚注册的channel。
                        Set selectedKeys = selector.selectedKeys();
                        Iterator i = selectedKeys.iterator();
                        while(i.hasNext())
                        {
                              s = (SelectionKey) i.next();
                              printKeyInfo(s);
                              debug("NBTest: Nr Keys in selector: " +selector.keys().size());
                              //一个key被处理完成后,就都被从就绪关键字(ready keys)列表中除去
                              i.remove();
                              if(s.isAcceptable())
                              {
                                    // 从channel()中取得我们刚刚注册的channel。
                                    Socket socket = ((ServerSocketChannel) s.channel()).accept();
                                    SocketChannel sc = socket.getChannel();
                                    sc.configureBlocking(false);
                                    sc.register(selector, SelectionKey.OP_READ SelectionKey.OP_WRITE);
                                    System.out.println(++channels);
                              }
                              else
                              {
                                    debug("NBTest: Channel not acceptable");
                              }
                        }
                  }
                  else
                  {
                        debug("NBTest: Select finished without any keys.");
                  }
            }
      }
      private static void debug(String s)
      {
            System.out.println(s);
      }
      private static void printKeyInfo(SelectionKey sk)
      {
            String s = new String();
            s = "Att: " + (sk.attachment() == null ? "no" : "yes");
            s += ", Read: " + sk.isReadable();
            s += ", Acpt: " + sk.isAcceptable();
            s += ", Cnct: " + sk.isConnectable();
            s += ", Wrt: " + sk.isWritable();
            s += ", Valid: " + sk.isValid();
            s += ", Ops: " + sk.interestOps();
            debug(s);
      }
      public static void main (String args[])
      {
            NBTest nbTest = new NBTest();
            try
            {
                  nbTest.startServer();
            }
            catch(Exception e)
            {
                  e.printStackTrace();
            }
      }
}


这是一段很常见的代码,注释已经写的很清楚,在此再总结一下。

首先,应用nio我们需要使用Selector,这是一个观察者模式的典型实现。我们的每个channel都必须注册到selector中才能被监听到。在sun.nio.ch.DefaultSelectorProvider(Selector的实现)内部有集合来存储我们的channel和对应的监听事件(已经封装为SelectionKey)。此时,我们的selector相当于观察者模式中的主题,一旦select方法被调用,就会遍历所有的观察者(此处是SelectionKey),调用它们统一的方法readyOps()来读取触发的事件,假设此处accept事件被触发,将会返回OP_ACCEPT(整型)。如果调用selector.selectedKeys(),将会以Set形式返回监听事件为OP_ACCEPT的所有SelectionKey,然后通过遍历集合,就可以找到我们的channel。




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论