Change again socket reconnect logic

This commit is contained in:
rafael-aero
2021-12-15 10:44:59 +01:00
parent 4a3a5c6aa4
commit 8cb235527d
3 changed files with 76 additions and 131 deletions

View File

@@ -25,6 +25,24 @@ namespace ElectronNET.API
/// </summary>
public static bool SocketDebug { get; set; }
/// <summary>
/// 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
/// </summary>
public static event Action OnSocketConnectFail;
internal static bool TryRaiseOnSocketConnectFail()
{
if (OnSocketConnectFail is object)
{
OnSocketConnectFail();
return true;
}
else
{
return false;
}
}
/// <summary>
/// Emitted when the user clicks on the dock on Mac

View File

@@ -93,7 +93,6 @@ namespace ElectronNET.API
private static readonly SemaphoreSlim _socketSemaphoreHandlers = new(1, 1);
private static AsyncManualResetEvent _connectedSocketEvent = new AsyncManualResetEvent();
private static TaskCompletionSource<SocketIO> _connectedSocketTask = new();
private static Dictionary<string, Action<SocketIOResponse>> _eventHandlers = new ();
@@ -109,7 +108,7 @@ namespace ElectronNET.API
private static async Task<SocketIO> 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

View File

@@ -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);
}
});