在JDK1.4版本增加的nio包以及其子包中有几个比较重要的基本概念,分别是缓冲器(Buffer),通道(Channel)还有选择器(Selector)。选择器允许一个线程处理多个通道,因而提高了系统的可 伸缩性以及灵活性。
不同的通道(比如SocketChannel,ServerSocketChannel)可以注册到同一个选择器中,然后赋予感兴趣的事件(总共4个:读(read),写(write),连接(connect),接受(accept) ),这样,当你感兴趣的事件激活时,选择器就会通知到你。
获取一个选择器对象很简单:
Selector sel = Selector.open(); //打开一个选择器
在该选择器上注册一个通道只需调用通道对象的register方法,第一个参数就是你的选择器对象,第二个参数指明你感兴趣的事件。
channel.register(selector,SelectionKey.OP_ACCEPT | SelectionKey.OP_CONNECT); // channel是一个通道对象,并且必须是非阻塞的
一共有4个兴趣事件:
public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;
如果有多个感兴趣的事件,用符号 | 连接即可。
下面用一个简单的实例来使用Selector。
服务器端和客户端的通信:
服务器端代码:
package org.snake.selector;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class Main {
public static void main(String[] args) throws Exception {
ServerSocketChannel ssc = ServerSocketChannel.open(); //打开一个ServerSocketChannel
ssc.configureBlocking(false); // 设置该通道为非阻塞模式
ssc.socket().bind(new InetSocketAddress(12345)); //绑定到服务器端口12345
Selector sel = Selector.open(); //打开一个选择器
ssc.register(sel, SelectionKey.OP_ACCEPT ); //将服务器通道注册到打开的选择器中,方法第二个参数指明感兴趣的位域(还有OP_READ,OP_WRITE,OP_CONNECT)
while(true) {
int acceptChannels = sel.select(); // 返回激活的感兴趣的通道数
if(acceptChannels == 0) continue;
Set<SelectionKey> selectedKeys = sel.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if(key.isAcceptable()) {
System.out.printf("isAcceptable\n");
ServerSocketChannel ssch = (ServerSocketChannel)key.channel();
SocketChannel sc = ssch.accept(); //sc 肯定不会为null
sc.configureBlocking(false);
sc.register(sel, SelectionKey.OP_READ);
String msg = "Welcome to NIO World";
ByteBuffer buff = ByteBuffer.allocate(128);
buff.put(msg.getBytes());
buff.flip();
sc.write(buff);
} else if(key.isConnectable()) {
} else if(key.isReadable()) {
SocketChannel ssch = (SocketChannel)key.channel();
ByteBuffer buff = ByteBuffer.allocate(128);
ssch.read(buff);
buff.flip();
while(buff.hasRemaining()) {
System.out.print((char)buff.get());
}
} else if(key.isWritable()) {
}
keyIterator.remove(); //处理完一个事件需删除掉,否则下次循环iterator仍会存在。
}
}
}
}
客户端:
输出: