private void SIOSocketWatchdog() { //We wait a moment and then start with current time as the first ping will last up to pingInterval Thread.Sleep(1000); lastPing = DateTime.Now; System.Random rng = new System.Random(); while (!cTokenSrc.IsCancellationRequested) { Thread.Sleep(500); if (Status == SIOStatus.RECONNECTING) { continue; //Wait for running attempt to end } if (DateTime.Now.Subtract(lastPing).TotalSeconds > (pingInterval + pingTimeout) || Socket.State != WebSocketState.Open) { if (cTokenSrc.IsCancellationRequested) { return; } //Send events for some constellations if (Socket.State == WebSocketState.Open) { SIODispatcher.Instance?.Enqueue(new Action(() => { RaiseSIOEvent("disconnect", "ping timeout"); })); } else if (Status == SIOStatus.CONNECTED) { SIODispatcher.Instance?.Enqueue(new Action(() => { RaiseSIOEvent("disconnect", "transport close"); })); } //Set the status flag if (enableAutoReconnect) { Status = SIOStatus.RECONNECTING; } else { Status = SIOStatus.DISCONNECTED; } //We need to stop the handler threads before reconnecting else we might see double reconnects as of exceptions raised in them if (WebSocketReaderThread != null && WebSocketReaderThread.IsAlive) { WebSocketReaderThread.Abort(); } if (WebSocketWriterThread != null && WebSocketWriterThread.IsAlive) { WebSocketWriterThread.Abort(); } if (enableAutoReconnect) { Thread.Sleep(300 + (ReconnectAttempts++ *1500) + rng.Next(50, 1000)); //Wait a moment in favor of the event handler and add some delay and jitter, not to hammer the server if (cTokenSrc.IsCancellationRequested) { return; } Connect(); //reconnect } return; //End the watchdog. It will be restarted after successful connect } } }
public override void Connect() { Task.Run(async() => { //Kill all remaining threads if (WebSocketReaderThread != null && WebSocketReaderThread.IsAlive) { WebSocketReaderThread.Abort(); } if (WebSocketWriterThread != null && WebSocketWriterThread.IsAlive) { WebSocketWriterThread.Abort(); } if (PingPongThread != null && PingPongThread.IsAlive) { PingPongThread.Abort(); } lock (Socket) { Socket = new ClientWebSocket(); } try { Uri baseUri = new Uri(targetAddress); Uri connectTarget = new Uri(baseUri.Scheme + "://" + baseUri.Host + ":" + baseUri.Port + "/socket.io/?EIO=3&transport=websocket" + (baseUri.Query.Length > 1 ? "&" + baseUri.Query.Substring(1) : "")); await Socket.ConnectAsync(connectTarget, cTokenSrc.Token); while (Socket.State != WebSocketState.Open) { Thread.Sleep(25); } ; } catch (Exception e) { //TODO Timeout? SocketIOManager.LogError(InstanceName + ": " + e.Message); SIODispatcher.Instance.Enqueue(new Action(() => { RaiseSIOEvent("connect_error", e.Message); })); Status = SIOStatus.ERROR; return; } try { WebSocketReaderThread = new Thread(new ThreadStart(SIOSocketReader)); WebSocketWriterThread = new Thread(new ThreadStart(SIOSocketWriter)); PingPongThread = new Thread(new ThreadStart(SIOSocketWatchdog)); WebSocketReaderThread.Start(); WebSocketWriterThread.Start(); PingPongThread.Start(); } catch (Exception e) { SocketIOManager.LogError("Exception while starting threads on " + InstanceName + ": " + e.ToString()); } //Thread.Sleep(100); Status = SIOStatus.CONNECTED; SIODispatcher.Instance.Enqueue(new Action(() => { RaiseSIOEvent("connect", null); })); }); base.Connect(); }