selector
会在事件发生后,加入 key
,但是,不会主动删除。
引入自写的方法类。
下载 JAVA 文件
netty 包
1 2 3 4 5
| <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.86.Final</version> </dependency>
|
服务端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| package com.redisc;
import lombok.extern.slf4j.Slf4j;
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 static com.redisc.ByteBufferUtil.debugRead;
@Slf4j(topic = "c.Test") public class Run {
public static void main(String[] args) throws IOException { Selector selector = Selector.open(); ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false);
SelectionKey sscKey = ssc.register(selector, 0, null); sscKey.interestOps(SelectionKey.OP_ACCEPT); log.debug("register key:{}", sscKey);
ssc.bind(new InetSocketAddress(8090)); while (true) { selector.select(); Iterator<SelectionKey> iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); log.debug("key:{}", key); if (key.isAcceptable()) { ServerSocketChannel channel = (ServerSocketChannel) key.channel(); SocketChannel sc = channel.accept(); sc.configureBlocking(false); SelectionKey scKey = sc.register(selector, 0, null); scKey.interestOps(SelectionKey.OP_READ); log.debug("{}", sc); } else if (key.isReadable()) { SocketChannel channel = (SocketChannel) key.channel(); ByteBuffer buffer = ByteBuffer.allocate(16); channel.read(buffer); buffer.flip(); debugRead(buffer); }
}
} }
}
|
客户端代码
1 2 3 4 5 6 7 8 9 10 11 12 13
| package com.redisc;
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SocketChannel;
public class Client { public static void main(String[] args) throws IOException { SocketChannel sc = SocketChannel.open(); sc.connect(new InetSocketAddress("localhost", 8090)); System.out.println("waiting..."); } }
|
在第 11 处打断点。
使用
开启服务端,开启客户端,并在 sc 变量处,使用
1
| sc.write(Charset.defaultCharset().encode("hi"))
|
服务端输出
1 2 3 4 5 6
| 10:44:32.211 [main] DEBUG c.Test - register key:sun.nio.ch.SelectionKeyImpl@497470ed 10:44:43.912 [main] DEBUG c.Test - key:sun.nio.ch.SelectionKeyImpl@497470ed 10:44:43.915 [main] DEBUG c.Test - java.nio.channels.SocketChannel[connected local=/127.0.0.1:8090 remote=/127.0.0.1:60566] 10:45:10.034 [main] DEBUG c.Test - key:sun.nio.ch.SelectionKeyImpl@497470ed Exception in thread "main" java.lang.NullPointerException at com.redisc.Run.main(Run.java:48)
|
解析
Selector.open();
sscKey.interestOps(SelectionKey.OP_ACCEPT);
- 在 selector 集合中添加
accept
事件
- 如图中
2
selector.selectedKeys()
- 当客户端建立连接,触发
accept
事件,会向 selectedKsys
添加事件
- 将
selectedKeys
的事件进行迭代循环处理
- 处理了
accept
事件,会将 selectedKeys
的事件标记去除
- 如图中
4
,虽然事件标记去除了,但是,事件仍然保留在 selectedKeys
中
- 接收到
read
事件
- 添加
read
事件 到 selectKeys
中
- 此时,事件循环再次迭代
- 但是,
accept
虽然存在于 selectKeys
中,但是,已经被使用,此时为 null
所以,我们要主动删除已经使用过的事件。