Close the WebSocket connection and reject all pending futures.
()
| 588 | * Close the WebSocket connection and reject all pending futures. |
| 589 | */ |
| 590 | public void close() { |
| 591 | if (this.verbose) { |
| 592 | System.out.println("WsClient closing: " + this.url); |
| 593 | } |
| 594 | if (this.channel != null && this.channel.isActive()) { |
| 595 | this.channel.writeAndFlush(new CloseWebSocketFrame()); |
| 596 | } |
| 597 | this.isConnected = false; |
| 598 | this.startedConnecting.set(false); |
| 599 | |
| 600 | // Wake the ping loop so it exits without waiting for the next keepAlive tick. |
| 601 | Thread pt = this.pingThread; |
| 602 | if (pt != null) { |
| 603 | pt.interrupt(); |
| 604 | } |
| 605 | |
| 606 | // Snapshot keys before mutating the map to avoid ConcurrentModificationException. |
| 607 | // Prefer the typed closeReason (set by Exchange.close()) over a bare |
| 608 | // RuntimeException, so consumers can `catch (ExchangeClosedByUser)` and |
| 609 | // tell deliberate shutdown from a remote-side disconnect. |
| 610 | Throwable rejectionReason = (this.closeReason != null) |
| 611 | ? this.closeReason |
| 612 | : new io.github.ccxt.errors.ExchangeClosedByUser("Connection closed by the user"); |
| 613 | var snapshot = new java.util.ArrayList<>(futuresMap().keySet()); |
| 614 | for (String key : snapshot) { |
| 615 | Future f = futuresMap().remove(key); |
| 616 | if (f != null && !f.isDone()) { |
| 617 | f.reject(rejectionReason); |
| 618 | } |
| 619 | } |
| 620 | |
| 621 | messageExecutor.shutdown(); |
| 622 | } |
| 623 | |
| 624 | // ─── Binary decompression (matches C# lines 393-471) ─── |
| 625 | // Note: Netty handles WebSocket frame aggregation before our handler sees frames, |