1、说明
java nio底层基于Reactor模型,采用多路复用IO模型,由一个专门的线程来处理所有的 IO 事件,并负责分发。基于事件驱动机制能够在事件到的时触发,而不是同步的去监视事件。 线程之间的通讯是通过 wait、notify等方式通讯,保证每次上下文切换都是有意义的,减少无谓的线程切换。
服务端和客户端各自维护一个管理通道的对象selector,其能检测一个或多个通道 (channel) 上的事件。例如:如果服务端selector上注册了读事件,某时刻客户端给服务端发送了一些数据(阻塞I/O这时会调用read()方法阻塞地读取数据),NIO的服务端会在selector中添加一个读事件,服务端的处理线程会轮询地访问selector,如果访问selector时发现有感兴趣的事件到达,则处理这些事件,如果没有感兴趣的事件到达,则处理线程会一直阻塞直到收到感兴趣的事件到达为止。
示例代码如下所示。希望对你有所帮助。
2、服务器端代码实现
package nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NioServer {
private Selector selector;
public static void main(String[] args) throws IOException {
NioServer server = new NioServer();
server.initServer(8098);
server.listener();
}
public void initServer(int port) throws IOException {
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
channel.socket().bind(new InetSocketAddress(port));
this.selector = Selector.open();
channel.register(selector, SelectionKey.OP_ACCEPT);
System.err.println();
}
public void listener() throws IOException {
while (true) {
selector.select();
Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
System.err.println("server size: " + this.selector.selectedKeys().size());
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (key.isAcceptable()) {
accept(key);
} else if (key.isReadable()) {
read(key);
} else if(key.isWritable()){
write(key);
}
}
}
}
private void accept(SelectionKey key) {
try {
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
channel.configureBlocking(false);
channel.write(ByteBuffer.wrap(new String("I am server, I send a msg").getBytes()));
channel.register(this.selector, SelectionKey.OP_READ);
} catch (ClosedChannelException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void write(SelectionKey key) {
try {
SocketChannel channel = (SocketChannel) key.channel();
String str = new String("server send msg to client...");
channel.configureBlocking(false);
channel.write(ByteBuffer.wrap(str.getBytes()));
System.err.println("服务器write: " + str);
channel.register(this.selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
private void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
channel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(100);
channel.read(buffer);
byte[] array = buffer.array();
String msg = new String(array).trim();
System.err.println("服务器read: " + msg);
channel.register(this.selector, SelectionKey.OP_WRITE);
}
}
3、客户端代码实现
package nio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NioClient {
// 通道管理器
private Selector selector;
private volatile int count = 0;
public static void main(String[] args) throws IOException {
NioClient client = new NioClient();
client.init("127.0.0.1", 8098);
client.listen();
}
public void init(String ip, int port) throws IOException {
// 获得一个Socket通道
SocketChannel channel = SocketChannel.open();
// 设置通道为非阻塞
channel.configureBlocking(false);
// 获得一个通道管理器
this.selector = Selector.open();
// 客户端连接服务器,其实方法执行并没有实现连接
channel.connect(new InetSocketAddress(ip, port));
// 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_CONNECT事件。
channel.register(selector, SelectionKey.OP_CONNECT);
}
public void listen() throws IOException {
// 轮询访问selector
while (true) {
//阻塞直到有事件
selector.select();
// 获得selector中选中的项的迭代器
Iterator ite = this.selector.selectedKeys().iterator();
while (ite.hasNext()) {
SelectionKey key = (SelectionKey) ite.next();
// 删除已选的key
ite.remove();
// 连接事件发生
if (key.isConnectable()) {
connect(key);
} else if (key.isReadable()) {
read(key);
}else if(key.isWritable()) {
write(key);
}
}
}
}
private void write(SelectionKey key) {
try {
SocketChannel channel = (SocketChannel) key.channel();
channel.configureBlocking(false);
String str = new String("server send msg to client...");
channel.write(ByteBuffer.wrap(str.getBytes()));
System.err.println("客户端write: " + str + " " +count++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
channel.register(this.selector, SelectionKey.OP_READ);
} catch (IOException e) {
e.printStackTrace();
}
}
private void connect(SelectionKey key) {
try {
SocketChannel channel = (SocketChannel) key.channel();
System.err.println("client size: " + this.selector.selectedKeys().size());
// 如果正在连接,则完成连接
if (channel.isConnectionPending()) {
channel.finishConnect();
}
// 设置成非阻塞
channel.configureBlocking(false);
// 在这里可以给服务端发送信息哦
channel.write(ByteBuffer.wrap(new String("I am client, I am send a request to server...").getBytes()));
// 在和服务端连接成功之后,为了可以接收到服务端的信息,需要给通道设置读的权限。
channel.register(this.selector, SelectionKey.OP_READ);
System.err.println("客户端connect: " +count++);
} catch (ClosedChannelException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 处理读取服务端发来的信息 的事件
s */
public void read(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
channel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(100);
channel.read(buffer);
byte[] array = buffer.array();
String msg = new String(array).trim();
System.err.println("客户端read: " + msg + " " +count++);
channel.register(this.selector, SelectionKey.OP_WRITE);
}
}