1. Java NIO简介
NIO(Non-Block I/O)是在JDK1.4之后引入的。NIO弥补了同步阻塞I/O的缺点。提供了高速、面向块的I/O。在NIO库中,所有的数据都是用缓冲区来处理的,在读取数据的时候,他是直接读到缓冲区中的:在写数据时,写到缓冲区中。任何时候访问NIO中的数据都是通过缓冲区进行操作。Java NIO由三个核心部分组成,分别是Buffers, Channel和Selector。
1.1 Buffers
Java为每一种基础的数据类型都提供了一个对应的缓冲区(除了Boolean外),具体有:
- ByteBuffer 字节缓冲区
- CharBuffer 字符缓冲区
- ShortBuffer 短整型缓冲区
- IntBuffer 整型缓冲区
- LongBuffer 长整形缓冲区
- FloatBuffer 浮点型缓冲区
- DoubleBuffer 双精度浮点型缓冲区
1.2 Channels
JAVA NIO中包括以下几种Channel:
- FileChannel
- DatagramChannel
- SocketChannel
- ServerSocketChannel
这些通道涵盖了UDP和TCP相关的网络IO,同时也包含有文件操作的IO。
1.3 Selector
Selector允许单线程处理多个Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。Java NIO中Selector采用epoll的机制进行实现,因此不存在有最大文件句柄的限制,因此只需要一个监听的线程就可以完成对成千上万个连接进行处理。
2. Java NIO服务器时序
按照上述步骤,时间
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 MultiplexerTimeServer implements Runnable{
private Selector selector;
private ServerSocketChannel servChannel;
private volatile boolean stop;
public MultiplexerTimeServer(int port){
try{
selector = Selector.open();
servChannel = ServerSocketChannel.open();
servChannel.configureBlocking(false);
servChannel.socket().bind(new InetSocketAddress(port), 1024);
servChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Timer Server Start! port == " + port);
}catch(IOException e){
e.printStackTrace();
System.exit(1);
}
}
public void stop(){
this.stop = true;
}
@Override
public void run() {
while(!stop){
try{
selector.select(1000);
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectionKeys.iterator();
SelectionKey key = null;
while(it.hasNext()){
key = it.next();
it.remove();
try{
handleInput(key);
}catch(Exception e){
key.cancel();
if(key.channel() != null){
key.channel().close();
}
}
}
}
catch(Throwable t){
t.printStackTrace();
}
}
if(selector != null){
try{
selector.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
private void handleInput(SelectionKey key) throws IOException{
if(key.isValid()){
if (key.isAcceptable()) {
// Accept the new connection
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
// Add the new connection to the selector
sc.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()) {
// Read the data
SocketChannel sc = (SocketChannel) key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readBytes = sc.read(readBuffer);
if (readBytes > 0) {
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
String body = new String(bytes, "UTF-8");
System.out.println("The time server receive order : "
+ body);
String currentTime = "QUERY TIME ORDER"
.equalsIgnoreCase(body) ? new java.util.Date(
System.currentTimeMillis()).toString()
: "BAD ORDER";
doWrite(sc, currentTime);
} else if (readBytes < 0) {
key.cancel(); // 对端链路关闭
sc.close();
}
else{
// 读到0字节,忽略
}
}
}
}
private void doWrite(SocketChannel channel, String response)throws IOException {
if (response != null && response.trim().length() > 0) {
byte[] bytes = response.getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);
writeBuffer.flip();
channel.write(writeBuffer);
}
}
}