Class UltiPanelWebSocketClient

java.lang.Object
org.java_websocket.WebSocketAdapter
org.java_websocket.AbstractWebSocket
org.java_websocket.client.WebSocketClient
com.ultikits.ultitools.websocket.UltiPanelWebSocketClient
All Implemented Interfaces:
Runnable, org.java_websocket.WebSocket, org.java_websocket.WebSocketListener

public class UltiPanelWebSocketClient extends org.java_websocket.client.WebSocketClient
UltiPanel WebSocket Client

新的WebSocket客户端,基于Java-WebSocket库

New WebSocket client based on Java-WebSocket library

  • Field Details

    • serverId

      private final String serverId
    • token

      private final String token
    • heartbeatExecutor

      private final ScheduledExecutorService heartbeatExecutor
    • isConnected

      private boolean isConnected
    • heartbeatTask

      private ScheduledFuture<?> heartbeatTask
    • gson

      private final com.google.gson.Gson gson
    • messageHandler

      private Consumer<com.google.gson.JsonObject> messageHandler
    • onConnectHandler

      private Runnable onConnectHandler
    • onDisconnectHandler

      private Runnable onDisconnectHandler
    • onErrorHandler

      private Consumer<String> onErrorHandler
    • onReconnectExhaustedHandler

      private Runnable onReconnectExhaustedHandler
    • MAX_RECONNECT_ATTEMPTS

      private static final int MAX_RECONNECT_ATTEMPTS
      See Also:
    • INITIAL_RECONNECT_DELAY_MS

      private static final long INITIAL_RECONNECT_DELAY_MS
      See Also:
    • reconnectAttempts

      private int reconnectAttempts
    • intentionalDisconnect

      private boolean intentionalDisconnect
    • HEARTBEAT_INTERVAL_SECONDS

      private static final long HEARTBEAT_INTERVAL_SECONDS
      心跳间隔(秒)。
      See Also:
    • PONG_TIMEOUT_MS

      private static final long PONG_TIMEOUT_MS
      判定「静默失效」的阈值:两个心跳周期没收到 pong。

      取两个周期而不是一个,是为了容忍单次丢包或一次调度抖动 —— 误判的代价是把一条好连接 踢掉重连,而漏判只是晚一个周期发现。

      See Also:
    • lastPingTime

      private volatile long lastPingTime
      最近一次发出 ping 的时间。
    • lastPongTime

      private volatile long lastPongTime
      最近一次收到 pong 的时间。0 表示从未收到过。
    • latencyMs

      private volatile long latencyMs
      最近一次测得的往返延迟,毫秒。
    • clock

      private volatile LongSupplier clock
      时钟。生产环境就是 System::currentTimeMillis,测试可以替换成假时钟。

      存在的唯一理由是让「超过阈值未收到 pong」这条判定可以被单元测试覆盖而不用 Thread.sleep —— 真等两个心跳周期是 120 秒,那种测试没人会留着。

  • Constructor Details

    • UltiPanelWebSocketClient

      public UltiPanelWebSocketClient(String url, String serverId, String token) throws URISyntaxException
      构造函数
      Parameters:
      url - WebSocket服务器URL
      serverId - 服务器ID
      token - 认证token
      Throws:
      URISyntaxException - 如果URL格式不正确
  • Method Details

    • getHeaders

      private static Map<String,String> getHeaders(String token)
    • connect

      public void connect()
      连接到WebSocket服务器
      Overrides:
      connect in class org.java_websocket.client.WebSocketClient
    • disconnect

      public void disconnect()
      断开WebSocket连接
    • sendMessage

      public void sendMessage(com.google.gson.JsonObject message)
      发送消息到服务器
      Parameters:
      message - JSON消息对象
    • sendPing

      public void sendPing()
      发送Ping消息
      Specified by:
      sendPing in interface org.java_websocket.WebSocket
      Overrides:
      sendPing in class org.java_websocket.client.WebSocketClient
    • subscribeToServer

      public void subscribeToServer(String serverId)
      订阅服务器状态
      Parameters:
      serverId - 要订阅的服务器ID
    • unsubscribeFromServer

      public void unsubscribeFromServer(String serverId)
      取消订阅服务器状态
      Parameters:
      serverId - 要取消订阅的服务器ID
    • setMessageHandler

      public void setMessageHandler(Consumer<com.google.gson.JsonObject> handler)
      设置消息处理器
      Parameters:
      handler - 消息处理器
    • setOnConnectHandler

      public void setOnConnectHandler(Runnable handler)
      设置连接成功处理器
      Parameters:
      handler - 连接成功处理器
    • setOnDisconnectHandler

      public void setOnDisconnectHandler(Runnable handler)
      设置断开连接处理器
      Parameters:
      handler - 断开连接处理器
    • setOnErrorHandler

      public void setOnErrorHandler(Consumer<String> handler)
      设置错误处理器
      Parameters:
      handler - 错误处理器
    • setOnReconnectExhaustedHandler

      public void setOnReconnectExhaustedHandler(Runnable handler)
      设置重连耗尽处理器(当所有重连尝试都失败后调用)
      Parameters:
      handler - 重连耗尽处理器
    • startHeartbeat

      private void startHeartbeat()
      启动心跳任务
    • heartbeatTick

      void heartbeatTick()
      一次心跳:先判活,再发 ping。

      「先判活」是本方法存在的理由。在此之前,连接健康的唯一判据是 socket 有没有断 —— 而一条 TCP 连接完全可以在不产生 onClose 的情况下静默失效(中间设备 超时、对端进程挂起、NAT 表项过期)。那种状态下 ping 发得出去、onClose 不触发、重连逻辑不启动,连接看着是好的,实际已经死了。

      判定为静默失效时**走 close() 而不是自己另起一套重连**:close 会触发 onClose,复用既有的重连状态机(含 #181 加的全局预算)。另起一套就会变成 第二个「决定要不要重连」的地方,那正是 #181 的成因。

    • recordPong

      private void recordPong()
      收到 pong 时调用:记录时间并算出往返延迟。

      onMessage(String) 在分发给 messageHandler 之前调用 —— pong 是链路层面的 事实,不该依赖上层处理器是否接线。

    • isAlive

      public boolean isAlive(long timeoutMs)
      根据 pong 应答判断连接是否还活着。

      从未收到过 pong 时返回 true。 这一条是刻意的:否则新建立的连接会在第一个 心跳周期就被判死。代价是这套机制发现不了「对端从来就不应答 pong」的情况 —— 但那是 安全的方向:面板若压根没实现 pong,我们不该把好连接反复踢掉。它能发现的是 「曾经在应答、后来不答了」,也就是静默失效。

      Parameters:
      timeoutMs - 判定阈值,毫秒
      Returns:
      最近一次 pong 在阈值之内,或从未收到过 pong
    • getLatencyMs

      public long getLatencyMs()
      最近一次测得的往返延迟,毫秒;尚未测到时为 -1。
      Returns:
      延迟毫秒数,或 -1
    • getLastPongTime

      public long getLastPongTime()
      最近一次收到 pong 的时间戳;从未收到过时为 0。
      Returns:
      时间戳,毫秒
    • setClock

      @Internal void setClock(LongSupplier clock)
      仅供测试替换时钟。
    • onOpen

      public void onOpen(org.java_websocket.handshake.ServerHandshake handshakedata)
      Specified by:
      onOpen in class org.java_websocket.client.WebSocketClient
    • onMessage

      public void onMessage(String message)
      Specified by:
      onMessage in class org.java_websocket.client.WebSocketClient
    • onClose

      public void onClose(int code, String reason, boolean remote)
      Specified by:
      onClose in class org.java_websocket.client.WebSocketClient
    • onError

      public void onError(Exception ex)
      Specified by:
      onError in class org.java_websocket.client.WebSocketClient