How to check websocket connection failed
When connect to websocket from client, I encounter a problem, that is how to check websocket connection failed.
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.