NIO进行服务端开发的一般步骤:
- 创建一个ServerSocketChannel,并配置它为非阻塞模式;
- 绑定监听,配置相关的TCP参数,比如backlog大小;
- 创建一个独立的I/O进程,用于轮询多路复用器Selector
- 创建Selector,将之前创建的ServerSocketChannel注册到Selector上,监听SelectionKey.ACCEPT事件
- 启动I/O线程,在一个循环体中执行Selecttor.selector()方法,轮询就绪的Channel
- 当轮询到就绪的Channel的时候,就需要对它的状态进行判断,如果是OP_ACCEPT状态,说明在这个时候,有客户端接入了,需要调用ServerSocketChannel.accept()方法接收新的客户端;
- 设置新接入的客户端链路SocketChannel为非阻塞模式,并配置一些TCP参数
- 将SocketChannel注册到Selector,监听OP_READ事件
- 如果轮询到了OP_READ事件,则说明在SocketChannel中有新的就绪数据包,这时候需要创建ByteBuffer读取数据包;
- 如果轮询到channel中的事件为OP_WRITE,说明还有数据包没有发送完成,需要继续进行发送
下面以一个简单的Demo来说明NIO的使用方法:
package study170301;
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 NioServer {
public void run() {
new Thread(new MultipexerTimeServer(8080)).start(); // 启动服务器
}
public static void main(String[] args) {
new NioServer().run();
}
class MultipexerTimeServer implements Runnable {
private Selector selector;
private ServerSocketChannel serverChannel;
private volatile boolean stop;
public MultipexerTimeServer(int port) {
try {
serverChannel = ServerSocketChannel.open();
selector = Selector.open();
serverChannel.configureBlocking(false);
serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册连接事件
serverChannel.socket().bind(new InetSocketAddress(port), 1024);
System.out.printf("Server is started in %s:\n", port);
} catch (IOException e) {
e.printStackTrace();
}
}
public void stop() {
this.stop = true;
}
private void handleEvent(SelectionKey key) {
// 只有在key合法的情况下,才对相应的事件做处理
if (key.isValid()) {
// 处理客户端的连接事件
if (key.isAcceptable()) {
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
try {
SocketChannel sc = ssc.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ); // 注册可读事件
} catch (IOException e) {
e.printStackTrace();
}
}
// 处理读事件
if (key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024); // 创建一个1024的空间
try {
int bytes = socketChannel.read(readBuffer);
if (bytes > 0) {
readBuffer.flip(); // 从缓存中出数据
byte[] bt = new byte[readBuffer.remaining()]; // 创建一个byte,用来装数据
readBuffer.get(bt);
System.out.println(new String(bt)); // 输出从客户端读到的数据
}
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
System.out.println("key is not valid...");
}
}
@Override
public void run() {
while (!stop) {
try {
selector.select(1000);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
SelectionKey key = null;
while (it.hasNext()) {
key = it.next();
it.remove();
// 在这里对key进行处理
handleEvent(key);
}
} catch (IOException e) {
e.printStackTrace();
} // 设置轮询扫描的时间间隔
}
}
}
}