From 8cb235527de813b32c7cfbfc618b5f3b51c81bbd Mon Sep 17 00:00:00 2001 From: rafael-aero Date: Wed, 15 Dec 2021 10:44:59 +0100 Subject: [PATCH] Change again socket reconnect logic --- ElectronNET.API/App.cs | 18 +++++ ElectronNET.API/BridgeConnector.cs | 107 ++++++----------------------- ElectronNET.Host/main.js | 82 ++++++++++------------ 3 files changed, 76 insertions(+), 131 deletions(-) diff --git a/ElectronNET.API/App.cs b/ElectronNET.API/App.cs index 6717e63..935a619 100644 --- a/ElectronNET.API/App.cs +++ b/ElectronNET.API/App.cs @@ -25,6 +25,24 @@ namespace ElectronNET.API /// public static bool SocketDebug { get; set; } + /// + /// Handle hard fails of connecting to the socket. The application must exit when this event is raised. + /// The default behavior is to exit with code 0xDEAD + /// + public static event Action OnSocketConnectFail; + + internal static bool TryRaiseOnSocketConnectFail() + { + if (OnSocketConnectFail is object) + { + OnSocketConnectFail(); + return true; + } + else + { + return false; + } + } /// /// Emitted when the user clicks on the dock on Mac diff --git a/ElectronNET.API/BridgeConnector.cs b/ElectronNET.API/BridgeConnector.cs index a2f1bb3..50ca7d2 100644 --- a/ElectronNET.API/BridgeConnector.cs +++ b/ElectronNET.API/BridgeConnector.cs @@ -93,7 +93,6 @@ namespace ElectronNET.API private static readonly SemaphoreSlim _socketSemaphoreHandlers = new(1, 1); private static AsyncManualResetEvent _connectedSocketEvent = new AsyncManualResetEvent(); - private static TaskCompletionSource _connectedSocketTask = new(); private static Dictionary> _eventHandlers = new (); @@ -109,7 +108,7 @@ namespace ElectronNET.API private static async Task GetSocket() { await _connectedSocketEvent.WaitAsync(); - return await _connectedSocketTask.Task; + return _socket; } public static bool IsConnected => _waitForConnection is Task task && task.IsCompletedSuccessfully; @@ -435,84 +434,46 @@ namespace ElectronNET.API var socket = new SocketIO($"http://localhost:{BridgeSettings.SocketPort}", new SocketIOOptions() { EIO = 3, - Reconnection = false, + Reconnection = true, ReconnectionAttempts = int.MaxValue, - ReconnectionDelay = 1000, + ReconnectionDelay = 500, ReconnectionDelayMax = 5000, RandomizationFactor = 0.5, ConnectionTimeout = TimeSpan.FromSeconds(10) }); socket.JsonSerializer = new CamelCaseNewtonsoftJsonSerializer(); + + _connectedSocketEvent.Reset(); socket.OnConnected += (_, __) => { - if (_socket == socket) - { - _connectedSocketTask.TrySetResult(socket); - _connectedSocketEvent.Set(); - Log("ElectronNET socket {1} connected on port {0}!", BridgeSettings.SocketPort, socket.Id); - } + _connectedSocketEvent.Set(); + Log("ElectronNET socket {1} connected on port {0}!", BridgeSettings.SocketPort, socket.Id); }; socket.OnReconnectAttempt += (_, __) => { - if (_socket == socket) - { - _connectedSocketEvent.Reset(); - _connectedSocketTask = new(); - Log("ElectronNET socket {1} is trying to reconnect on port {0}...", BridgeSettings.SocketPort, socket.Id); - } + _connectedSocketEvent.Reset(); + Log("ElectronNET socket {1} is trying to reconnect on port {0}...", BridgeSettings.SocketPort, socket.Id); }; socket.OnReconnectError += (_, ex) => { - if (_socket == socket) - { - Log("ElectronNET socket {1} failed to connect {0}", ex, socket.Id); - _connectedSocketEvent.Reset(); - _connectedSocketTask = new(); - _socket = null; - } + _connectedSocketEvent.Reset(); + Log("ElectronNET socket {1} failed to connect {0}", ex, socket.Id); }; socket.OnReconnected += (_, __) => { - if (_socket == socket) - { - _connectedSocketTask.TrySetResult(socket); - _connectedSocketEvent.Set(); - Log("ElectronNET socket {1} reconnected on port {0}...", BridgeSettings.SocketPort, socket.Id); - } + _connectedSocketEvent.Set(); + Log("ElectronNET socket {1} reconnected on port {0}...", BridgeSettings.SocketPort, socket.Id); }; socket.OnDisconnected += (_, reason) => { - if (_socket == socket) - { - _connectedSocketEvent.Reset(); - _connectedSocketTask = new(); - _socket = null; - - Task.Run(async () => - { - await Task.Yield(); - EnsureSocketTaskIsCreated(); - }); - - Log("ElectronNET socket {2} disconnected with reason {0}, trying to reconnect on port {1}!", reason, BridgeSettings.SocketPort, socket.Id); - } - else - { - try - { - socket.Dispose(); - } - catch - { - //Ignore - } - } + _connectedSocketEvent.Reset(); + Log("ElectronNET socket {2} disconnected with reason {0}, trying to reconnect on port {1}!", reason, BridgeSettings.SocketPort, socket.Id); }; _socket = socket; @@ -521,23 +482,15 @@ namespace ElectronNET.API { try { - if (!socket.Connected) - { - if (_socket == socket) - { - await socket.ConnectAsync(); - _connectedSocketTask.TrySetResult(socket); - _connectedSocketEvent.Set(); - } - } - return; + await socket.ConnectAsync(); } catch (Exception e) { - await Task.Delay(1000); - if (!socket.Connected) + Console.WriteLine(e.ToString()); + + if(!App.TryRaiseOnSocketConnectFail()) { - LogError(e, "Failed to connect"); + Environment.Exit(0xDEAD); } } }); @@ -548,26 +501,6 @@ namespace ElectronNET.API { throw new Exception("Missing Socket Port"); } - - if(_backgroundMonitorThread is null) - { - _backgroundMonitorThread = new Thread(() => - { - while (true) - { - Thread.Sleep(5_000); - - if (HybridSupport.IsElectronActive) - { - EnsureSocketTaskIsCreated(); - } - } - }); - _backgroundMonitorThread.IsBackground = true; - _backgroundMonitorThread.Name = "Monitor Electron Socket"; - _backgroundMonitorThread.Priority = ThreadPriority.Lowest; - _backgroundMonitorThread.Start(); - } } } else diff --git a/ElectronNET.Host/main.js b/ElectronNET.Host/main.js index b1a4e67..b0d3dfc 100644 --- a/ElectronNET.Host/main.js +++ b/ElectronNET.Host/main.js @@ -229,7 +229,7 @@ function startSocketApiBridge(port) { server = require('http').createServer(); io = require('socket.io')(); - io.attach(server, { pingTimeout: 5000, pingInterval: 10000 }); + io.attach(server, { pingTimeout: 10000, pingInterval: 5000 }); server.listen(port, 'localhost'); server.on('listening', function () { @@ -263,50 +263,32 @@ function startSocketApiBridge(port) { } }); - let firstTime = (global['electronsocket'] === undefined); + //We only hook to events on app on the first initialization of each component + let firstTime = (global['electronsocket'] == undefined); - if (firstTime) { - console.log("First socket connection"); - global['electronsocket'] = socket; - socket.setMaxListeners(0); - } + global['electronsocket'] = socket; + socket.setMaxListeners(0); console.log('.NET connected on socket ' + socket.id + ' on ' + new Date()); - if (appApi === undefined) appApi = require('./api/app')(socket, app, firstTime); - if (browserWindows === undefined) browserWindows = require('./api/browserWindows')(socket, app, firstTime); - if (commandLine === undefined) commandLine = require('./api/commandLine')(socket, app); - if (autoUpdater === undefined) autoUpdater = require('./api/autoUpdater')(socket, app); - if (ipc === undefined) ipc = require('./api/ipc')(socket); - if (menu === undefined) menu = require('./api/menu')(socket); - if (dialogApi === undefined) dialogApi = require('./api/dialog')(socket); - if (notification === undefined) notification = require('./api/notification')(socket); - if (tray === undefined) tray = require('./api/tray')(socket); - if (webContents === undefined) webContents = require('./api/webContents')(socket); - if (globalShortcut === undefined) globalShortcut = require('./api/globalShortcut')(socket); - if (shellApi === undefined) shellApi = require('./api/shell')(socket); - if (screen === undefined) screen = require('./api/screen')(socket); - if (clipboard === undefined) clipboard = require('./api/clipboard')(socket); - if (browserView === undefined) browserView = require('./api/browserView').browserViewApi(socket); - if (powerMonitor === undefined) powerMonitor = require('./api/powerMonitor')(socket); - if (nativeThemeApi === undefined) nativeThemeApi = require('./api/nativeTheme')(socket); - if (dock === undefined) dock = require('./api/dock')(socket); - - socket.on('register-app-open-file-event', (id) => { - global['electronsocket'] = socket; - - app.on('open-file', (event, file) => { - - if (global['electronsocket'] === socket) { - event.preventDefault(); - global['electronsocket'].emit('app-open-file' + id, file); - } - }); - - if (launchFile) { - global['electronsocket'].emit('app-open-file' + id, launchFile); - } - }); + appApi = require('./api/app')(socket, app, firstTime); + browserWindows = require('./api/browserWindows')(socket, app, firstTime); + commandLine = require('./api/commandLine')(socket, app); + autoUpdater = require('./api/autoUpdater')(socket, app); + ipc = require('./api/ipc')(socket); + menu = require('./api/menu')(socket); + dialogApi = require('./api/dialog')(socket); + notification = require('./api/notification')(socket); + tray = require('./api/tray')(socket); + webContents = require('./api/webContents')(socket); + globalShortcut = require('./api/globalShortcut')(socket); + shellApi = require('./api/shell')(socket); + screen = require('./api/screen')(socket); + clipboard = require('./api/clipboard')(socket); + browserView = require('./api/browserView').browserViewApi(socket); + powerMonitor = require('./api/powerMonitor')(socket); + nativeThemeApi = require('./api/nativeTheme')(socket); + dock = require('./api/dock')(socket); socket.on('splashscreen-destroy', () => { if (splashScreen) { @@ -315,17 +297,29 @@ function startSocketApiBridge(port) { } }); + socket.on('register-app-open-file-event', (id) => { + global['electronsocket'] = socket; + + app.on('open-file', (event, file) => { + event.preventDefault(); + global['electronsocket'].emit('app-open-file' + id, file); + }); + + if (launchFile) { + socket.emit('app-open-file' + id, launchFile); + } + }); + socket.on('register-app-open-url-event', (id) => { global['electronsocket'] = socket; app.on('open-url', (event, url) => { event.preventDefault(); - global['electronsocket'].emit('app-open-url' + id, url); }); if (launchUrl) { - global['electronsocket'].emit('app-open-url' + id, launchUrl); + socket.emit('app-open-url' + id, launchUrl); } }); @@ -415,7 +409,7 @@ function startAspCoreBackend(electronPort) { else if (os.platform() === 'darwin') { //There is a bug on the updater on macOS never quiting and starting the update process //We give Squirrel.Mac enough time to access the update file, and then just force-exit here - setTimeout(() => app.exit(0), 10_000); + setTimeout(() => app.exit(0), 30_000); } });