Closed wangjiejacques closed 3 years ago
@wangjiejacques The reason why tearDownConnection(error:)
is called in the failed
case (vs reportErrorOrDisconnection(_ error:)
in the waiting
case) is because by the time the connection has transitioned to the failed
state, the disconnection event will have already been reported where the error occurred, at one of the other call-sites of reportErrorOrDisconnection(_ error:)
(e.g. sending or receiving data, or when expecting a Pong response to a Ping).
I verified this was the behaviour during my testing (i.e. previously I was also reporting the disconnection as a result of transitioning to the failed
connection state, and seeing the disconnection being reported twice).
You've raised a good point however, in that I should make this behaviour clear via some comments in stateDidChange(to:)
.
Hi @danielrbrowne , thanks for you quick response,
you are right, reportErrorOrDisconnection
was called before failed
, in this case, we will have bug of reconnect
.
Here is my case,
After all these 4 steps, 2 things will happed in the NWWebSocket
.
A. self.reportErrorOrDisconnection(error)
will be called by connection?.receiveMessage { [weak self] (data, context, _, error) in
inside the method public func listen()
. (👍 you are right, reportErrorOrDisconnection
is called.)
Method trace: reportErrorOrDisconnection
→ webSocketDidDisconnect
→ attemptReconnect
→ connect
B. tearDownConnection(error: error)
will be called inside the method private func stateDidChange(to state: NWConnection.State)
, and finally we will have this connection = nil
.
Method trace: tearDownConnection
→ connection = nil
The bug I have is that, when we try to reconnect
in the A, sometimes the step B connection = nil
is not called yet, which cause the method connect
do nothing.(connection == nil
is false), maybe we need to set connection = nil
somewhere inside reportErrorOrDisconnection
.
open func connect() {
if connection == nil {
connection = NWConnection(to: endpoint, using: parameters)
connection?.stateUpdateHandler = stateDidChange(to:)
connection?.betterPathUpdateHandler = betterPath(isAvailable:)
connection?.viabilityUpdateHandler = viabilityDidChange(isViable:)
listen()
connection?.start(queue: connectionQueue)
}
}
some code maybe related to https://github.com/pusher/pusher-websocket-swift
here is the code about reonnect in the PusherWebsocketDelegate.swift
public func webSocketDidDisconnect(connection: WebSocketConnection,
closeCode: NWProtocolWebSocket.CloseCode,
reason: Data?) {
// Handles setting channel subscriptions to unsubscribed wheter disconnection
// is intentional or not
if connectionState == .disconnecting || connectionState == .connected {
for (_, channel) in self.channels.channels {
channel.subscribed = false
}
}
self.connectionEstablishedMessageReceived = false
self.socketConnected = false
updateConnectionState(to: .disconnected)
guard !intentionalDisconnect else {
self.delegate?.debugLog?(message: PusherLogger.debug(for: .intentionalDisconnection))
return
}
// Log the disconnection
logDisconnection(closeCode: closeCode, reason: reason)
// Attempt reconnect if possible
// `autoReconnect` option is ignored if the closure code is within the 4000-4999 range
if case .privateCode = closeCode {} else {
guard self.options.autoReconnect else {
return
}
}
guard reconnectAttemptsMax == nil || reconnectAttempts < reconnectAttemptsMax! else {
self.delegate?.debugLog?(message: PusherLogger.debug(for: .maxReconnectAttemptsLimitReached))
return
}
attemptReconnect(closeCode: closeCode)
}
@wangjiejacques Thanks for the detailed description of the race condition issue. I will look into a solution for this.
@wangjiejacques I believe this PR should resolve the race condition you were seeing: #24
I'll include it in the next releases of the NWWebSocket library and the Pusher Channels SDK.
@danielrbrowne Thank you very much!! 👍 do you know when will you release Pusher Channels SDK ?
@wangjiejacques It will as soon as possible follow code review by my colleague.
The race condition fix has been released in v0.5.1 of NWWebSocket
(which itself has been included in v9.1.1 of the Pusher Channels Swift SDK).
In the
NWWebSocket.swift
from the following code, we will call
reportErrorOrDisconnection
forwaiting(let error)
, which will callwebSocketDidDisconnect
for certain errors. On the other hand, we will calltearDownConnection
forfailed(let error)
, which will not callwebSocketDidDisconnect
even if it's disconnected. Why this?