NIO概述
NIO是JDK 1.4开始引入的。弥补了旧的IO的不足之处。
NIO的3个重要组件是缓冲区(Buffer)、通道(Channel)、选择器(Selector)。
Buffer使得可以更加灵活的读/写字节流。处理数据时,可以方便的在Buffer里前后移动,提高了处理过程的灵活性。
Channel将之前的单向的输入/输出流整合在一起,既可以读,也可以写。
Selector可以监听多个Channel,一个线程就可以应付成千上万的Client连接,轮询得到就绪的事件进行后续处理,原理是基于OS提供的多路复用技术。
实例代码
效果
Server
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 EchoServer {
private Selector selector;
private ServerSocketChannel serverSocketChannel;
private volatile boolean stop;
EchoServer() throws IOException {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(40000),1024);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("Server监听在40000端口");
while(!stop){
selector.select(1000);
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> it = selectedKeys.iterator();
SelectionKey key = null;
while(it.hasNext()){
key = it.next();
it.remove();;
handler(key);
}
}
}
private void handler(SelectionKey key) throws IOException {
if(key.isValid()){
if(key.isAcceptable()){
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector,SelectionKey.OP_READ);
}
if(key.isReadable()){
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer readBytes = ByteBuffer.allocate(1024);
int readCount = socketChannel.read(readBytes);
if(readCount > 0){
readBytes.flip();
byte[] bytes = new byte[readBytes.remaining()];
readBytes.get(bytes);
String receiveMsg = new String(bytes,"UTF-8");
if("bye".equals(receiveMsg)){
stop();
return;
}
String responseString ="服务端收到:" + receiveMsg;
System.out.println(responseString);
responseString = "服务器返回" + receiveMsg;
ByteBuffer responseBuffer = ByteBuffer.allocate(responseString.getBytes().length);
responseBuffer.put(responseString.getBytes());
responseBuffer.flip();
socketChannel.write(responseBuffer);
}
}
}
}
public void stop(){
this.stop = true;
}
public static void main(String[] args) {
try {
new EchoServer();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client
package com.sss.nio.echo_server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class EchoClient extends Thread {
private Selector selector;
private SocketChannel socketChannel;
private volatile boolean stop;
SocketChannel channel;
public EchoClient() throws IOException {
selector = Selector.open();
socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
}
@Override
public void run() {
try {
boolean connected = socketChannel.connect(new InetSocketAddress("127.0.0.1", 40000));
if (connected) {
socketChannel.register(selector, SelectionKey.OP_WRITE);
} else {
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
while (!stop) {
selector.select(1000);
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
handler(key);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void handler(SelectionKey key) throws IOException {
if (key.isValid()) {
channel = (SocketChannel) key.channel();
if (key.isConnectable()) {
if (channel.finishConnect()) {
channel.register(selector, SelectionKey.OP_WRITE);
}
}
if (key.isReadable()) {
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
int readCount = channel.read(readBuffer);
if (readCount > 0) {
readBuffer.flip();
byte[] bytes = new byte[readBuffer.remaining()];
readBuffer.get(bytes);
System.out.println("client收到消息:" + new String(bytes, "UTF-8"));
} else if (readCount < 0) {
key.cancel();
channel.close();
}
channel.register(selector, SelectionKey.OP_WRITE);
}
if (key.isWritable()) {
BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
if (((msg = localReader.readLine()) != null)) {
ByteBuffer writeBuffer = ByteBuffer.allocate(msg.getBytes().length);
writeBuffer.put(msg.getBytes());
writeBuffer.flip();
socketChannel.write(writeBuffer);
socketChannel.register(selector, SelectionKey.OP_READ);
}
}
}
}
private void sendMsg(String msg) throws IOException {
ByteBuffer responseBuffer = ByteBuffer.allocate(msg.getBytes().length);
responseBuffer.put(msg.getBytes());
responseBuffer.flip();
socketChannel.write(responseBuffer);
channel.register(selector, SelectionKey.OP_READ);
}
public static void main(String[] args) {
EchoClient echoClient = null;
try {
echoClient = new EchoClient();
echoClient.start();
System.out.println("echoClient已启动");
} catch (IOException e) {
e.printStackTrace();
}
}
}