事实上Java nio引入了异步机制,异步I/O 在Linux上有 select poll epoll,支持多路复用。在Java里就是通过nio的一整套类来实现的,主要有:
ByteBuffer
SocketChannel
ServerSocketChannel
Selector
SelectionKey
注册:
channel调用 register来向selector注册,
移除:
SelectionKey.cancel //仅仅把selectionkey 对应的channel从selector中移除,socket仍然处于活动状态,需要手动关闭。
channel.close()//从selector移除,加关闭channel关联的socket对象
//得到一个selector
Selector select = Selector.open();
ServerSocketChannel ss = ServerSocketChannel.open();
//必须配置成non-blocking
ss.configureBlocking(false);
//绑定到一个地址
ss.socket().bind( new InetSocketAddress("0.0.0.0", 5858));
//把channel注册selector,此时会有一个SelectionKey跟这个channel关联
//当有连接进来时,select可以返回selectedKeys
ss.register(select, SelectionKey.OP_ACCEPT);
while(select.select() >= 0){
//返回处于就绪状态的keys
Set<SelectionKey> keyReady = select.selectedKeys();
//轮询每一个就绪状态的key,通过key可以得到channel,也可以通过key从selector里删除
for( Iterator<SelectionKey> iter = keyReady.iterator(); iter.hasNext(); ){
SelectionKey sk = iter.next();
iter.remove();
System.out.println("cancel" + select.keys().size());
SocketChannel client = null;
if ( sk.channel() instanceof ServerSocketChannel){
ss= (ServerSocketChannel)sk.channel();
client = ss.accept();
System.out.println("accept: " + client.socket());
//client.socket().close();
//必须配置成non-blocking
client.configureBlocking(false);
//把非被动socket注册到selector
client.register(select, SelectionKey.OP_READ);
}
else if (sk.channel() instanceof SocketChannel ){
SocketChannel ch = (SocketChannel)sk.channel();
ByteBuffer recvBuf = ByteBuffer.allocate(100);
//必须用nio中的ByteBuffer,否则会出错
int len = ch.read(recvBuf);
if ( len < 0){
//移除前必须关闭socket,否则会有很多socket处于打开状态
ch.socket().close();
//从selector移除channel
sk.cancel();
continue;
}
recvBuf.flip();
ch.write(recvBuf);
//sk.cancel();
System.out.println("cancel" + select.keys().size() + "len " + len);
len = ch.read(recvBuf);
//关闭channel,此时会关闭channel对应的socket,同时从selector中移除这个channel
// ch.close();
System.out.println("len" + len);
}
else{
System.out.println("error change");
}
}
}
System.out.println("end======");