多个SocketChannel注册Selector统一管理

虽然我们可以自己处理每一个Socket事件,比如读写数据,不过更常规的方式是注册一个选择器。这个选择器侦听着数据的变化事件。 每个注册的通道都有自己的SelectionKey,用这个可以区分到底是哪个通道产生了事件。

看代码
  1. package net.java2000.nio;
  2. import java.io.IOException;
  3. import java.net.InetSocketAddress;
  4. import java.nio.ByteBuffer;
  5. import java.nio.channels.SelectionKey;
  6. import java.nio.channels.Selector;
  7. import java.nio.channels.SocketChannel;
  8. import java.util.Iterator;
  9. /**
  10.  * 多个SocketChannel注册Selector。
  11.  * 
  12.  * @author 赵学庆,Java世纪网(java2000.net)
  13.  * 
  14.  */
  15. public class SocketChannelSelector {
  16.   public static SocketChannel createSocketChannel(String hostName, int port)
  17.       throws IOException {
  18.     SocketChannel sChannel = SocketChannel.open();
  19.     sChannel.configureBlocking(false);
  20.     sChannel.connect(new InetSocketAddress(hostName, port));
  21.     return sChannel;
  22.   }
  23.   // 2个连接注册的选择器关键字
  24.   static SelectionKey key1;
  25.   static SelectionKey key2;
  26.   public static void main(String[] args) {
  27.     // 1个选择器,注册2个Socket 通道
  28.     Selector selector = null;
  29.     try {
  30.       // 创建选择器
  31.       selector = Selector.open();
  32.       // 创建2个通道
  33.       SocketChannel sChannel1 = createSocketChannel("163.net"25);
  34.       SocketChannel sChannel2 = createSocketChannel("mail.csdn.net"25);
  35.       // 注册选择器,侦听所有的事件
  36.       key1 = sChannel1.register(selector, sChannel1.validOps());
  37.       key2 = sChannel2.register(selector, sChannel1.validOps());
  38.     } catch (IOException e) {
  39.     }
  40.     // 等待事件的循环
  41.     while (true) {
  42.       try {
  43.         // 等待
  44.         selector.select();
  45.       } catch (IOException e) {
  46.         break;
  47.       }
  48.       // 所有事件列表
  49.       Iterator it = selector.selectedKeys().iterator();
  50.       // 处理每一个事件
  51.       while (it.hasNext()) {
  52.         // 得到关键字
  53.         SelectionKey selKey = it.next();
  54.         // 删除已经处理的关键字
  55.         it.remove();
  56.         try {
  57.           // 处理事件
  58.           processSelectionKey(selKey);
  59.         } catch (IOException e) {
  60.           // 处理异常
  61.           selKey.cancel();
  62.         }
  63.       }
  64.     }
  65.   }
  66.   public static void processSelectionKey(SelectionKey selKey) throws IOException {
  67.     ByteBuffer buf = ByteBuffer.allocateDirect(1024);
  68.     // 确认连接正常
  69.     if (selKey.isValid() && selKey.isConnectable()) {
  70.       // 得到通道
  71.       SocketChannel sChannel = (SocketChannel) selKey.channel();
  72.       // 是否连接完毕?
  73.       boolean success = sChannel.finishConnect();
  74.       if (!success) {
  75.         // 异常
  76.         selKey.cancel();
  77.       }
  78.     }
  79.     // 如果可以读取数据
  80.     if (selKey.isValid() && selKey.isReadable()) {
  81.       // 得到通道
  82.       SocketChannel sChannel = (SocketChannel) selKey.channel();
  83.       if (sChannel.read(buf) > 0) {
  84.         // 转到最开始
  85.         buf.flip();
  86.         while (buf.remaining() > 0) {
  87.           System.out.print((char) buf.get());
  88.         }
  89.         // 也可以转化为字符串,不过需要借助第三个变量了。
  90.         // buf.get(buff, 0, numBytesRead);
  91.         // System.out.println(new String(buff, 0, numBytesRead, "UTF-8"));
  92.         // 复位,清空
  93.         buf.clear();
  94.       }
  95.     }
  96.     // 如果可以写入数据
  97.     if (selKey.isValid() && selKey.isWritable()) {
  98.       // 得到通道
  99.       SocketChannel sChannel = (SocketChannel) selKey.channel();
  100.       // 区分2个侦听器的关键字
  101.       // 我这里只写一次数据。
  102.       if (!s1 && key1.equals(selKey)) {
  103.         System.out.println("channel1 write data..");
  104.         buf.clear();
  105.         buf.put("HELO localhost/n".getBytes());
  106.         buf.flip();
  107.         sChannel.write(buf);
  108.         s1 = true;
  109.       } else if (!s2 && key2.equals(selKey)) {
  110.         System.out.println("channel2 write data..");
  111.         buf.clear();
  112.         buf.put("HELO localhost/n".getBytes());
  113.         buf.flip();
  114.         sChannel.write(buf);
  115.         s2 = true;
  116.       }
  117.     }
  118.   }
  119.   // 判断已经写过数据的标志
  120.   static boolean s1 = false;
  121.   static boolean s2 = false;
  122. }

一个运行的效果
channel1 write data..
220 Coremail SMTP(Anti Spam) System (163net[040302])
250 bjfee_app2
channel2 write data..
220 csdn.net ESMTP "can't tell you"
250 csdn.net


结论
  这样做,我们的多个数据通道就可以统一管理,分别处理器读写操作。

你可能感兴趣的:(Java)