java.nio (NIO stands for non-blocking I/O[1]) is a collection of Java programming language APIs that offer features for intensive I/O operations. It was introduced with the J2SE 1.4 release of Java by Sun Microsystems to complement an existing standard I/O. NIO was developed under the Java Community Process as JSR 51.[2] An extension to NIO that offers a new file system API, called NIO.2, was released with Java SE 7 ("Dolphin").[3]
NIO的模型
Java的传统IO操作是面向流的。而对流的读写是阻塞式的;也就是说,当一个线程调用流的read()或者write()方法时,该线程将被阻塞,直到成功读取或写入这些数据。
JDK1.4引入了新的Java IO库,被称为NIO(New I/O),支持阻塞式和非阻塞式良好两种形式。其非阻塞式模式下,允许一个线程在进行读操作时,总是读取当前可用的数据。当暂时没有数据可用时,就不执行,而不是阻塞等待。
NIO包括Channel(通道)、Buffer(缓冲区)、Selector(多路复用器)三大部分。
用JAVA NIO实现一个最简单的HTTP服务器。
不管浏览器请求什么,都展示Hello, world在浏览器中。
注意点:
(1).在处理ACCEPT事件后,只需要往Selector中注册READ操作。不需要注册WRITE操作,因为WRITE操作是随时都可用的。
(2).处理完读操作之后,要用SelectionKey.interestOps(SelectionKey.WRITE_OP)切换为写模式。
(3).处理完响应操作,才能关掉客户端的SocketChannel。
OP_WRITE is almost always ready. It rarely if ever makes sense to register for OP_READ and OP_WRITE at the same time.
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 一个最简单的HTTP服务器。 <br>
* 不管浏览器请求什么,都返回Hello, world
*/
public class MyHttpServer {
public static final int DEFAULT_PORT = 8087;
public static final String CRLF = "\r\n";
private final static Logger logger = LoggerFactory.getLogger(MyHttpServer.class);
public void exeute() throws IOException {
ServerSocketChannel serverChannel = null;
Selector selector = null;
try {
serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress(DEFAULT_PORT));
serverChannel.configureBlocking(false);
selector = Selector.open();
// 只需要注册ACCEPT操作
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
} catch (IOException e) {
e.printStackTrace();
}
while (true) {
int n = selector.select();
if (n < 1) {
continue;
}
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
if (key.isAcceptable()) {
handleAccetable(key, selector);
}
if (key.isReadable()) {
handleReadable(key);
// 重要!! 将SelectionKey切换为写模式
key.interestOps(SelectionKey.OP_WRITE);
}
if (key.isWritable()) {
handleWritable(key);
// 处理完响应操作,才能关掉客户端的SocketChannel
key.channel().close();
}
} catch (Exception ex) {
key.cancel();
}
}
}
}
private void handleAccetable(SelectionKey key, Selector selector) throws IOException {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
InetSocketAddress socketAddress = (InetSocketAddress) client.getRemoteAddress();
String ip = socketAddress.getAddress().getHostAddress();
logger.info("Accepted connection from " + ip + ":" + socketAddress.getPort());
client.configureBlocking(false);
// 只需要注册READ操作
SelectionKey clientKey = client.register(selector, SelectionKey.OP_READ);
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
clientKey.attach(byteBuffer);
}
private void handleReadable(SelectionKey key) throws IOException {
logger.info("handleReadable");
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
byteBuffer.rewind();
int n = client.read(byteBuffer);
if (n < 1) {
return;
}
// 打印HTTP请求报文出来
byteBuffer.flip();
byte[] array = byteBuffer.array();
String str = new String(array, 0, n);
logger.info("Receive request from client:----------------");
logger.info(str);
}
private void handleWritable(SelectionKey key) throws IOException {
logger.info("handleWritable");
SocketChannel client = (SocketChannel) key.channel();
client.configureBlocking(false);
ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
String contenxt = "Hello, world";
StringBuilder sb = new StringBuilder();
sb.append("HTTP/1.1 200 OK");
sb.append(CRLF);
sb.append("Date:Sun, 21 Apr 2013 15:12:46 GMT");
sb.append(CRLF);
sb.append("Server:Apache");
sb.append(CRLF);
sb.append("Connection:Close");
sb.append(CRLF);
sb.append("Content-Type:text/plain; charset=ISO-8859-1");
sb.append(CRLF);
sb.append("Content-Length: " + contenxt.length());
sb.append(CRLF);
sb.append(CRLF);
sb.append(contenxt); // 内容为Hello, world
sb.append(CRLF);
String str2 = sb.toString();
byteBuffer.put(str2.getBytes());
byteBuffer.flip();
// 输出HTTP响应报文
client.write(byteBuffer);
}
public static void main(String[] args) throws IOException {
new MyHttpServer().exeute();
}
}
效果图
参考:
[1] sockets - Java nio SelectionKey.register and interestops - Stack Overflow