How to check websocket connection failed

When connect to websocket from client, I encounter a problem, that is how to check websocket connection failed.

How to check websocket connection failed
Photo by Rodion Kutsaev on Unsplash

When connect to websocket from client, I encounter a problem, that is how to check websocket connection failed.

There is an example in kmm implementation, which is kmm-websocket.

Android side

In the androidMain , the websocket connection code is like below:val webClient = OkHttpClient().newBuilder().build()

In order to add ping check, we can call pingInterval method on OkHttpClient.Build, the following code will check the connection every 25 seconds.val webClient = OkHttpClient().newBuilder().pingInterval(25, TimeUnit.Seconds).build()

With this setting, the websocket will report error if connection is lost for 25 seconds.

iOS side

In iosMain, the code for connection is like below:actual fun openSocket(listener: PlatformSocketListener) {
       val urlSession = NSURLSession.sessionWithConfiguration(
               configuration = NSURLSessionConfiguration.defaultSessionConfiguration(),
               delegate = object : NSObject(), NSURLSessionWebSocketDelegateProtocol {
                   override fun URLSession(
                           session: NSURLSession,
                           webSocketTask: NSURLSessionWebSocketTask,
                           didOpenWithProtocol: String?
                   ) {
                       listener.onOpen()
                   }
                   override fun URLSession(
                           session: NSURLSession,
                           webSocketTask: NSURLSessionWebSocketTask,
                           didCloseWithCode: NSURLSessionWebSocketCloseCode,
                           reason: NSData?
                   ) {
                       listener.onClosed(didCloseWithCode.toInt(), reason.toString())
                   }
               },
               delegateQueue = NSOperationQueue.currentQueue()
       )webSocket = urlSession.webSocketTaskWithURL(socketEndpoint)
       listenMessages(listener)
       webSocket?.resume()
   }

But there are no place to set pingInterval, I find that there is a function sendPingWithPongReceiveHandler , so I call this method from client, but the timeout will occur on different time, the timeout times are vary from 1 minute to 5 minutes.

How can I implement the check just like okhttp, and the client can get notification when connection is lost?

Investigation

socket.io-client-swift

I find that there is another library socket.io-client-swift , in this library, it has a function to checkPings , the source code is like below:private func checkPings() {
       let pingInterval = self.pingInterval ?? 25_000
       let deadlineMs = Double(pingInterval + pingTimeout) / 1000
       let timeoutDeadline = DispatchTime.now() + .milliseconds(pingInterval + pingTimeout)engineQueue.asyncAfter(deadline: timeoutDeadline) {[weak self, id = self.sid] in
           // Make sure not to ping old connections
           guard let this = self, this.sid == id else { return }if abs(this.lastCommunication?.timeIntervalSinceNow ?? deadlineMs) >= deadlineMs {
               this.closeOutEngine(reason: "Ping timeout")
           } else {
               this.checkPings()
           }
       }
   }

So this library is check timeout with the lastCommunication time and the timeout with code, not the request timeout.

okhttp

I also check the code on okhttp , the code in this library is like below:internal fun writePingFrame() {
   val writer: WebSocketWriter
   val failedPing: Int
   synchronized(this) {
     if (failed) return
     writer = this.writer ?: return
     failedPing = if (awaitingPong) sentPingCount else -1
     sentPingCount++
     awaitingPong = true
   }if (failedPing != -1) {
     failWebSocket(SocketTimeoutException("sent ping but didn't receive pong within " +
         "${pingIntervalMillis}ms (after ${failedPing - 1} successful ping/pongs)"), null)
     return
   }try {
     writer.writePing(ByteString.EMPTY)
   } catch (e: IOException) {
     failWebSocket(e, null)
   }
 }

In this library, also check timeout with code, it has a variable to record awaitingPong, if this variable is true on the next ping, it means that the client didn’t get the pong response, it will close the connection if ping failed.

Solution

After investigate the code of okhttps and socket.io-client-swift, I find that I can also check whether the ping is timeout with code, but not the timeout setting on the session or request.


Resources