选择器:
选择器解决的问题:选择器类管理着一个被注册的通道集合的信息和他们的就绪状态。通道和选择器一起被注册,并且使用选择器来更新通道的就绪状态。
可选择通道(SelectableChannel),这个抽象类提供了通道的可选择性所需要的公共方法。FileChannel对象不是可选择的,因为他们没有继承selectableChannel.所有socket通道都是可选择的,包括从管道(Pipe)对象中获取的通道,SelectableChannel可以被注册到selector对象上,一个通道可以被注册到多个选择器上,但是对每个选择器而言只能被注册一次。
选择键(selectionKey)
选择键封装了通道与选择器的注册关系。选择键对象被SelectableChannel.register返回并提供一个表示这种注册关系的标记。通道在被注册到一个选择器上之前,必须先设置为非阻塞模式(通过调用configureBlocking(false))。
调用可选择通道的register()方法会将他注册到一个选择器上。如果试图注册一个处于阻塞模式的通道,register()将抛出未检查的IlllegalBlockingModeException异常。此外,通道一旦被注册,就不能回到阻塞状态。试图这样做,则在调用configureBlocking()方法是抛出IlllegalBlockingModeException异常。并且试图注册一个已经关闭的selectablechannel实例的话,也将抛出异常(losedchannelException).
键的interest(感兴趣的操作)集合和ready(已经准备好的操作)集合是和特定的通道相关的,每个通道的实现,将定义他自己的选择键类。在register()方法中可以构造它并且将他传递给所提供的选择器对象。
使用非阻塞I/O编写服务器处理程序,大致步骤为:
1、向selector对象注册感兴趣的事件;
2、从selector中获取感兴趣的事件。
3、根据不同的事件进行相应的处理。
以下代码建立监控三个socket通道的选择器:
Selector selector=Selector.Open();
Channel1.register(selector,SelectionKey.OP_READ);
Channel2.register(selector,Selectionkey.OP_WRITE);
Channel3.register(selector,Selectionkey.OP_WRITE|OP_READ);
readCount =selector.select(10000);
Select方法是阻塞方法,直到过了十秒或者至少有一个通道的I/O操作准备好了。
在SelectionKey中用静态常量定义了四中I/O操作:OP_READ 1、OP_WRITE4、OP_CONNECT 8、OP_ACCEPT 16.任意相加都不相同,可以通过validOps()方法返回值确定SelectableChannel支持的操作。当通道关闭时,所有相关的建会自动取消,当选择器关闭时,所有被注册到该选择器的通道都将被注销,并且相关的键将立即被无效化。一旦键无效化,调用相关的方法将抛出CancelledKeyException异常。
服务端package it.com.Jerome;
import java.io.IOException;
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;
public class SelectorServer {
private Selector selector;
private ServerSocketChannel serverChannel=null;
private int keys =0;
public void start() {
try{
SelectorServer ss=new SelectorServer();
ss.initServer();
ss.listen();
}catch(Exception e){
e.printStackTrace();
}
}
public void initServer() throws IOException {
this.selector=Selector.open();
serverChannel =ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress("127.0.0.1",8888));
serverChannel.configureBlocking(false);
SelectionKey key = serverChannel.register(this.selector, SelectionKey.OP_ACCEPT);
}
public void listen() throws Exception {
System.out.println("服务已经成功启动!");
while(true){
keys=this.selector.select();
Iterator it=this.selector.selectedKeys().iterator();
if(keys>0){
while(it.hasNext()){
SelectionKey key =(SelectionKey)it.next();
it.remove();
if(key.isAcceptable()){
serverChannel = (ServerSocketChannel)key.channel();
//和客户端链接的通道
SocketChannel channel = serverChannel.accept();
channel.configureBlocking(false);
channel.write(ByteBuffer.wrap(new String("hello client").getBytes()));
channel.register(this.selector, SelectionKey.OP_READ);
}else if(key.isReadable()){
read(key);
}
}
}
else{
System.out.println("selector finished without any keys");
}
}
}
private void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel)key.channel();
ByteBuffer buff=ByteBuffer.allocate(1024);
int len=channel.read(buff);
String msg = "服务器收到的信息为:"+new String(buff.array(),0,len);
System.out.println(msg);
}
public static void main(String args[]){
new SelectorServer().start();
}
}
客户端:package it.com.Jerome;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class SelectorClient {
private Selector selector;
private ByteBuffer outBuff = ByteBuffer.allocate(1024);
private ByteBuffer inBuff = ByteBuffer.allocate(1024);
private int keys=0;
private SocketChannel channel=null;
public void initClient() throws Exception{
channel = SocketChannel.open();
selector=selector.open();
channel.configureBlocking(false);
channel.connect(new InetSocketAddress("127.0.0.1",8888));
channel.register(this.selector, SelectionKey.OP_CONNECT);
}
public void listen() throws Exception{
while(true){
keys =this.selector.select();
if(keys>0){
Iterator it = this.selector.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key=(SelectionKey)it.next();
if(key.isConnectable()){
SocketChannel channel=(SocketChannel)key.channel();
if(channel.isConnectionPending()){
channel.finishConnect();
System.out.println("完成链接!");
}
channel.register(this.selector, SelectionKey.OP_WRITE);
}else if(key.isWritable()){
SocketChannel channel=(SocketChannel)key.channel();
outBuff.clear();
System.out.println("客户端正在写数据!");
channel.write(outBuff.wrap("我是clientA".getBytes()));
channel.register(this.selector, SelectionKey.OP_READ);
System.out.println("客户端数据写完成!");
}else if(key.isReadable()){
SocketChannel channel=(SocketChannel)key.channel();
inBuff.clear();
channel.read(inBuff);
System.out.println("==>"+new String(inBuff.array()));
System.out.println("client finish read data!");
}
}
}else{
System.out.println("没有找到感兴趣的事件!");
}
}
}
public static void main(String[] args) {
new SelectorClient().start();
}
public void start() {
try{
initClient();
listen();
}catch(Exception e){
e.printStackTrace();
}
}
}