您的位置:首页 > 其它

Nonblocking I/O

2009-04-24 16:39 183 查看

Nonblocking I/O

在Nonblocking I/O中最重要的也就是三个类

引用
java.nio.channels.SelectableChannel
java.nio.channels.Selector
java.nio.channels.SelectionKey

1 SelectableChannel

默认情况下channels是阻塞的.我们可以设置一个channel为nonblock的,可是并不是所有的channel都可以设置为nonblock的,比如file channels就不能设置为nonblock.所有提供nonblocking I/O的类都是SelectableChannel的子类.

我们能够通过configureBlocking方法来设置channel为nonblocking mode.

public abstract SelectableChannel configureBlocking(boolean block)
throws IOException


当你将一个channel置于nonblocking mode时,你就不能立即读或者写它,你需要将它注册到一个Selector对象.也就是说此时这个channel什么时候读和写,已经完全交给这个selector来控制了.

我们使用register方法来注册一个channel到一个Selector对象.

public abstract SelectionKey register(Selector selector, int operations)
throws ClosedChannelException


这里要注意的是一个channel对象只能被注册到一个Selector对象一次.

register方法的第二个参数指的是这个Selector如何处理这个channel,这里有4中操作方式:
reading, writing, accepting和connecting.其中我们可以用| 来加入多个操作.Server socket channels是accepting.

channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);


register还有一个三个参数的方法

public abstract SelectionKey register(
Selector selector, int operations, Object attachment)
throws ClosedChannelException


在这里第三个参数可以为空,如果为空也就相当于2个参数的方法了.这个参数也就是存储一个channel读或者写的数据.一般都使用一个ByteBuffer对象来存储这个.

register返回一个SelectionKey对象,来表示这个channel与Selector的对象之间的独一无二的关系.

register和configureBlocking都是线程安全的.

其他的方法,去看jdk文档就行了.

2 Selector

Selector类可以说是nonblocking I/O中最重要的一个类.因为他能够控制你的程序中的channel什么时候准备好被存取.我们能够使用它的静态方法open来创建一个 Selector对象:

public static Selector open( ) throws IOException


当你想要得到Selector那些channel准备好了的时候,也就是说那些channel能够非阻塞的读或者写.你可以调用下面的三个方法中的一个:

public abstract int selectNow( ) throws IOException
public abstract int select( ) throws IOException
public abstract int select(long timeout) throws IOException


selectNow方法是非阻塞的,而另外两个方法是阻塞的.selectNow方法将会马上返回,哪怕它并没有select到任何东西.而另外两个方法则是select到了channel才会返回,或者线程中断,或者超时也会返回.

其实这3个方法说白了,也就是选择出一个准备好io操作的channel的集合.返回值就是准备好的channel的个数.

还有一个方法selectedKeys,返回一个java.util.Set ,这个set里面包含所有注册在这个Selector上的准备好io操作的channel的key.

public abstract Set<SelectionKey> selectedKeys();


这里要注意,一个Selector,当你使用完他时应当调用他的close方法来关闭它:

public abstract void close( ) throws IOException


在一些系统里,一个select()可能会阻塞一个线程一段时间,此时另外的线程能够调用Selector's wakeup方法来使select方法立即返回.jdk文档里是这样写的:

引用
If another thread is currently blocked in an invocation of the select() or select(long) methods then that invocation will return immediately. If no selection operation is currently in progress then the next invocation of one of these methods will return immediately unless the selectNow() method is invoked in the meantime. In any case the value returned by that invocation may be non-zero. Subsequent invocations of the select() or select(long) methods will block as usual unless this method is invoked again in the meantime.

3 SelectionKey

java.nio.channels.SelectionKey 封装了一个channel注册到一个Selector上的所有信息 每一个SelectionKey封装了下列的信息:

引用
The channel

The Selector

An arbitrary object attachment, normally used to point to the data the channel is reading or writing

The operations the channels is interested in performing

The operations the channel is currently ready to perform without blocking (or, more accurately, was ready to perform when select( ) was last called)

这些信息在SelectionKey中大部分都是通过set或者get方法来进行设置和得到的.

readyOps方法可以得到这个SelectionKey的mode,也就是说,得到它是read, write, connect, 还是accept.

public abstract int readyOps( )


这里还有一套更方便的api来判断这个:

public final boolean isReadable( )
public final boolean isWritable( )
public final boolean isConnectable( )
public final boolean isAcceptable( )


如果你想读或者写channel,你必须得通过channel方法来得到这个channel然后才能进行操作.

public abstract SelectableChannel channel( )


这里要注意一个channel能有多个key,但是每一个key只能有一个channel.

这里还有两个重要的方法:

public final Object attach(Object ob)
public final Object attachment( )


第一个方法attach联系这个对象到这个key(或者用三个参数的register方法也是一样的),第二个方法attachment是从key中得到这个对象.

你还能通过cancel方法来撤销一个注册:

public abstract void cancel( )


这边有一个简单的例子:

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;
import java.util.Set;

public class NonBlockDataStuffer {
private static byte[] data = new byte[256];

public static void main(String[] args) throws IOException {
for (int i = 0; i < data.length; i++)
data[i] = (byte) i;
ServerSocketChannel server = ServerSocketChannel.open();
server.configureBlocking(false);
server.socket().bind(new InetSocketAddress(9000));
Selector selector = Selector.open( );
ByteBuffer buffer=ByteBuffer.allocate(256);
server.register(selector, SelectionKey.OP_ACCEPT,buffer);
while (true) {
selector.select();
Set<SelectionKey> readyKeys = selector.selectedKeys( );
Iterator<SelectionKey> iterator = readyKeys.iterator( );
while (iterator.hasNext( )) {
SelectionKey key = iterator.next( );
iterator.remove();
try {
if (key.isAcceptable()) {
SocketChannel client = server.accept();
System.out.println("Accepted connection from " + client);
client.configureBlocking(false);
ByteBuffer source = ByteBuffer.wrap(data);
SelectionKey key2 = client.register(selector, SelectionKey.OP_WRITE);
key2.attach(source);
}
else if (key.isWritable( )) {
SocketChannel client = (SocketChannel) key.channel( );
ByteBuffer output = (ByteBuffer) key.attachment();
if (!output.hasRemaining( )) {
output.rewind( );
}
client.write(output);
}
}
catch (IOException ex) {
key.cancel( );
selector.close();
try {
key.channel().close();
}
catch (IOException cex) {}
}
}

}
}
}


其实nio还有一个很重要的类那就是pipe,那个下次再说了...

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: