本文详细介绍组成非阻塞通信的几大类:Buffer、Channel、Selector、SelectionKey
非阻塞通信的流程
- ServerSocketChannel通过open方法获取ServerSocketChannel,通过ServerSocketChannel设置为非阻塞模式,再通过ServerSocketChannel获取socket,绑定服务进程监听端口。服务启动成功。
- 然后就是非阻塞通信的精髓了,Selector通过静态的open()方法获取到Selector,然后ServerSocketChannel注册Selection.OP_ACCEPT事件到Selector上。
- Selector就会监控事件发生,Selector通过select()监控已发生的SelectionKey对象的数目,通过selectKeys()方法返回对应的selectionKey对象集合。遍历该集合得到相应的selectionKey对象,通过该对象的channel()方法获取关联的ServerSocketChannel对象, 通过selector()方法就可以获取关联的Selector对象,并移除相应的selectionKey。
- 通过上面获取的ServerSocketChannel执行accept()方法获取SocketChannel,再通过SocketChannel设置为非阻塞模式,在将SocketChannel注册到上面创建的Selector上,注册
SelectionKey.OP_READ |SelectionKey.OP_WRITE
事件。 - Selector将在监控对应上面绑定的事件,监控到对应的事件的话执行读和写的操作。
示例代码:
上面描述了服务端非阻塞方式通信的一个流程,下面通过具体代码实现:
/**
* 非阻塞模式
*
*/
public class EchoServer2 {
private Selector selector = null;
private ServerSocketChannel serverSocketChannel = null;
private int port = 8001;
private Charset charset = Charset.forName("UTF-8");
public EchoServer2() throws IOException {
selector = Selector.open();
serverSocketChannel = ServerSocketChannel.open();
//服务器重启的时候,重用端口
serverSocketChannel.socket().setReuseAddress(true);
//设置非阻塞模式
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(port));
System.out.println("服务器启动成功");
}
/**
* 服务方法
*/
public void service() throws IOException {
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0) {
Set readyKes = selector.selectedKeys();
Iterator it = readyKes.iterator();
while (it.hasNext()) {
SelectionKey key = null;
try {
key = (SelectionKey) it.next();
it.remove();
if (key.isAcceptable()) {
System.out.println("连接事件");
//连接事件
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = ssc.accept();
System.out.println("接收到客户连接,来自:" + socketChannel.socket().getInetAddress() +
" : " + socketChannel.socket().getPort());
socketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
socketChannel.register(selector, SelectionKey.OP_READ |
SelectionKey.OP_WRITE, buffer);
} else if (key.isReadable()) {
//接收数据
receive(key);
} else if (key.isWritable()) {
//发送数据
send(key);
}
} catch (IOException e) {
e.printStackTrace();
try {
if (key != null) {
key.cancel();
key.channel().close();
}
}catch (IOException ex){
ex.printStackTrace();
}
}
}
}
}
private void send(SelectionKey key) throws IOException {
ByteBuffer buffer = (ByteBuffer) key.attachmen