|
4 | 4 |
|
5 | 5 | import java.io.IOException;
|
6 | 6 | import java.net.SocketAddress;
|
7 |
| -import java.net.SocketTimeoutException; |
8 |
| -import java.nio.channels.SelectableChannel; |
9 | 7 | import java.nio.channels.SelectionKey;
|
10 | 8 | import java.nio.channels.Selector;
|
| 9 | +import java.util.List; |
| 10 | +import java.util.Set; |
| 11 | +import java.util.concurrent.CopyOnWriteArrayList; |
11 | 12 | import lombok.extern.slf4j.Slf4j;
|
12 | 13 | import org.xbill.DNS.utils.hexdump;
|
13 | 14 |
|
14 | 15 | @Slf4j
|
15 | 16 | class Client {
|
16 |
| - |
17 |
| - protected long endTime; |
18 |
| - protected SelectionKey key; |
19 |
| - |
20 | 17 | /** Packet logger, if available. */
|
21 | 18 | private static PacketLogger packetLogger = null;
|
22 | 19 |
|
23 |
| - protected Client(SelectableChannel channel, long endTime) throws IOException { |
24 |
| - boolean done = false; |
25 |
| - Selector selector = null; |
26 |
| - this.endTime = endTime; |
27 |
| - try { |
28 |
| - selector = Selector.open(); |
29 |
| - channel.configureBlocking(false); |
30 |
| - key = channel.register(selector, SelectionKey.OP_READ); |
31 |
| - done = true; |
32 |
| - } finally { |
33 |
| - if (!done && selector != null) { |
34 |
| - selector.close(); |
35 |
| - } |
36 |
| - if (!done) { |
37 |
| - channel.close(); |
38 |
| - } |
| 20 | + private static volatile boolean run; |
| 21 | + private static Thread selectorThread; |
| 22 | + private static List<Runnable> timeoutTasks = new CopyOnWriteArrayList<>(); |
| 23 | + static Selector selector; |
| 24 | + |
| 25 | + protected interface KeyProcessor { |
| 26 | + void processReadyKey(SelectionKey key); |
| 27 | + } |
| 28 | + |
| 29 | + protected static void start() throws IOException { |
| 30 | + if (run) { |
| 31 | + return; |
| 32 | + } |
| 33 | + |
| 34 | + run = true; |
| 35 | + selector = Selector.open(); |
| 36 | + selectorThread = new Thread(Client::runSelector); |
| 37 | + selectorThread.setDaemon(true); |
| 38 | + selectorThread.setName("dnsjava NIO selector"); |
| 39 | + selectorThread.start(); |
| 40 | + } |
| 41 | + |
| 42 | + protected static void close() throws Exception { |
| 43 | + if (!run) { |
| 44 | + return; |
39 | 45 | }
|
| 46 | + |
| 47 | + run = false; |
| 48 | + timeoutTasks.clear(); |
| 49 | + selector.wakeup(); |
| 50 | + selector.close(); |
| 51 | + selectorThread.join(); |
40 | 52 | }
|
41 | 53 |
|
42 |
| - protected static void blockUntil(SelectionKey key, long endTime) throws IOException { |
43 |
| - long timeout = endTime - System.currentTimeMillis(); |
44 |
| - int nkeys = 0; |
45 |
| - if (timeout > 0) { |
46 |
| - nkeys = key.selector().select(timeout); |
47 |
| - } else if (timeout == 0) { |
48 |
| - nkeys = key.selector().selectNow(); |
| 54 | + private static void runSelector() { |
| 55 | + while (run) { |
| 56 | + try { |
| 57 | + if (selector.select(100) == 0) { |
| 58 | + timeoutTasks.forEach(Runnable::run); |
| 59 | + continue; |
| 60 | + } |
| 61 | + |
| 62 | + processReadyKeys(); |
| 63 | + } catch (IOException e) { |
| 64 | + log.error("A selection operation failed", e); |
| 65 | + } |
49 | 66 | }
|
50 |
| - if (nkeys == 0) { |
51 |
| - throw new SocketTimeoutException(); |
| 67 | + } |
| 68 | + |
| 69 | + static void addSelectorTimeoutTask(Runnable r) { |
| 70 | + timeoutTasks.add(r); |
| 71 | + } |
| 72 | + |
| 73 | + private static void processReadyKeys() { |
| 74 | + Set<SelectionKey> keys = selector.selectedKeys(); |
| 75 | + for (SelectionKey key : keys) { |
| 76 | + KeyProcessor t = (KeyProcessor) key.attachment(); |
| 77 | + t.processReadyKey(key); |
52 | 78 | }
|
53 | 79 | }
|
54 | 80 |
|
55 |
| - protected static void verboseLog( |
56 |
| - String prefix, SocketAddress local, SocketAddress remote, byte[] data) { |
57 |
| - if (log.isDebugEnabled()) { |
58 |
| - log.debug(hexdump.dump(prefix, data)); |
| 81 | + static void verboseLog(String prefix, SocketAddress local, SocketAddress remote, byte[] data) { |
| 82 | + if (log.isTraceEnabled()) { |
| 83 | + log.trace(hexdump.dump(prefix, data)); |
59 | 84 | }
|
60 | 85 | if (packetLogger != null) {
|
61 | 86 | packetLogger.log(prefix, local, remote, data);
|
62 | 87 | }
|
63 | 88 | }
|
64 | 89 |
|
65 |
| - void cleanup() throws IOException { |
66 |
| - key.selector().close(); |
67 |
| - key.channel().close(); |
68 |
| - } |
69 |
| - |
70 | 90 | static void setPacketLogger(PacketLogger logger) {
|
71 | 91 | packetLogger = logger;
|
72 | 92 | }
|
|
0 commit comments