Fixed reconnects due to connection loss
This commit is contained in:
@@ -177,7 +177,7 @@ namespace Discord.Net.WebSockets
|
|||||||
|
|
||||||
if (packetLength > 0 && endpoint.Equals(_endpoint))
|
if (packetLength > 0 && endpoint.Equals(_endpoint))
|
||||||
{
|
{
|
||||||
if (_state != (int)ConnectionState.Connected)
|
if (_state != ConnectionState.Connected)
|
||||||
{
|
{
|
||||||
if (packetLength != 70)
|
if (packetLength != 70)
|
||||||
return;
|
return;
|
||||||
@@ -247,7 +247,7 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
while (!cancelToken.IsCancellationRequested && _state != (int)ConnectionState.Connected)
|
while (!cancelToken.IsCancellationRequested && _state != ConnectionState.Connected)
|
||||||
Thread.Sleep(1);
|
Thread.Sleep(1);
|
||||||
|
|
||||||
if (cancelToken.IsCancellationRequested)
|
if (cancelToken.IsCancellationRequested)
|
||||||
@@ -399,7 +399,7 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
case VoiceOpCodes.Ready:
|
case VoiceOpCodes.Ready:
|
||||||
{
|
{
|
||||||
if (_state != (int)ConnectionState.Connected)
|
if (_state != ConnectionState.Connected)
|
||||||
{
|
{
|
||||||
var payload = (msg.Payload as JToken).ToObject<VoiceReadyEvent>(_serializer);
|
var payload = (msg.Payload as JToken).ToObject<VoiceReadyEvent>(_serializer);
|
||||||
_heartbeatInterval = payload.HeartbeatInterval;
|
_heartbeatInterval = payload.HeartbeatInterval;
|
||||||
@@ -488,7 +488,7 @@ namespace Discord.Net.WebSockets
|
|||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
ThrowError();
|
_taskManager.ThrowException();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ namespace Discord
|
|||||||
private readonly DiscordConfig _config;
|
private readonly DiscordConfig _config;
|
||||||
|
|
||||||
/// <summary> Returns the current connection state of this client. </summary>
|
/// <summary> Returns the current connection state of this client. </summary>
|
||||||
public ConnectionState State => (ConnectionState)_state;
|
public ConnectionState State => _state;
|
||||||
private int _state;
|
private ConnectionState _state;
|
||||||
|
|
||||||
/// <summary> Gives direct access to the underlying DiscordAPIClient. This can be used to modify objects not in cache. </summary>
|
/// <summary> Gives direct access to the underlying DiscordAPIClient. This can be used to modify objects not in cache. </summary>
|
||||||
public DiscordAPIClient APIClient => _api;
|
public DiscordAPIClient APIClient => _api;
|
||||||
@@ -145,7 +145,7 @@ namespace Discord
|
|||||||
var settings = new JsonSerializerSettings();
|
var settings = new JsonSerializerSettings();
|
||||||
_webSocket.Connected += (s, e) =>
|
_webSocket.Connected += (s, e) =>
|
||||||
{
|
{
|
||||||
if (_state == (int)ConnectionState.Connecting)
|
if (_state == ConnectionState.Connecting)
|
||||||
EndConnect();
|
EndConnect();
|
||||||
};
|
};
|
||||||
_webSocket.Disconnected += (s, e) =>
|
_webSocket.Disconnected += (s, e) =>
|
||||||
@@ -277,7 +277,7 @@ namespace Discord
|
|||||||
if (!_sentInitialLog)
|
if (!_sentInitialLog)
|
||||||
SendInitialLog();
|
SendInitialLog();
|
||||||
|
|
||||||
if (State != (int)ConnectionState.Disconnected)
|
if (State != ConnectionState.Disconnected)
|
||||||
await Disconnect().ConfigureAwait(false);
|
await Disconnect().ConfigureAwait(false);
|
||||||
|
|
||||||
_token = token;
|
_token = token;
|
||||||
@@ -293,7 +293,8 @@ namespace Discord
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _taskManager.Stop().ConfigureAwait(false);
|
await _taskManager.Stop().ConfigureAwait(false);
|
||||||
_state = (int)ConnectionState.Connecting;
|
_taskManager.ClearException();
|
||||||
|
_state = ConnectionState.Connecting;
|
||||||
|
|
||||||
var gatewayResponse = await _api.Gateway().ConfigureAwait(false);
|
var gatewayResponse = await _api.Gateway().ConfigureAwait(false);
|
||||||
string gateway = gatewayResponse.Url;
|
string gateway = gatewayResponse.Url;
|
||||||
@@ -326,7 +327,7 @@ namespace Discord
|
|||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
_webSocket.ThrowError(); //Throws data socket's internal error if any occured
|
_webSocket.TaskManager.ThrowException(); //Throws data socket's internal error if any occured
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -335,15 +336,15 @@ namespace Discord
|
|||||||
_lock.Release();
|
_lock.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await Disconnect().ConfigureAwait(false);
|
_taskManager.SignalError(ex, true);
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private void EndConnect()
|
private void EndConnect()
|
||||||
{
|
{
|
||||||
_state = (int)ConnectionState.Connected;
|
_state = ConnectionState.Connected;
|
||||||
_connectedEvent.Set();
|
_connectedEvent.Set();
|
||||||
RaiseConnected();
|
RaiseConnected();
|
||||||
}
|
}
|
||||||
@@ -352,8 +353,9 @@ namespace Discord
|
|||||||
public Task Disconnect() => _taskManager.Stop();
|
public Task Disconnect() => _taskManager.Stop();
|
||||||
|
|
||||||
private async Task Cleanup()
|
private async Task Cleanup()
|
||||||
{
|
{
|
||||||
if (Config.UseMessageQueue)
|
_state = ConnectionState.Disconnecting;
|
||||||
|
if (Config.UseMessageQueue)
|
||||||
{
|
{
|
||||||
MessageQueueItem ignored;
|
MessageQueueItem ignored;
|
||||||
while (_pendingMessages.TryDequeue(out ignored)) { }
|
while (_pendingMessages.TryDequeue(out ignored)) { }
|
||||||
@@ -373,8 +375,8 @@ namespace Discord
|
|||||||
_token = null;
|
_token = null;
|
||||||
|
|
||||||
_state = (int)ConnectionState.Disconnected;
|
_state = (int)ConnectionState.Disconnected;
|
||||||
_disconnectedEvent.Set();
|
|
||||||
_connectedEvent.Reset();
|
_connectedEvent.Reset();
|
||||||
|
_disconnectedEvent.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnReceivedEvent(WebSocketEventEventArgs e)
|
private void OnReceivedEvent(WebSocketEventEventArgs e)
|
||||||
@@ -822,11 +824,11 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
switch (_state)
|
switch (_state)
|
||||||
{
|
{
|
||||||
case (int)ConnectionState.Disconnecting:
|
case ConnectionState.Disconnecting:
|
||||||
throw new InvalidOperationException("The client is disconnecting.");
|
throw new InvalidOperationException("The client is disconnecting.");
|
||||||
case (int)ConnectionState.Disconnected:
|
case ConnectionState.Disconnected:
|
||||||
throw new InvalidOperationException("The client is not connected to Discord");
|
throw new InvalidOperationException("The client is not connected to Discord");
|
||||||
case (int)ConnectionState.Connecting:
|
case ConnectionState.Connecting:
|
||||||
throw new InvalidOperationException("The client is connecting.");
|
throw new InvalidOperationException("The client is connecting.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ namespace Discord
|
|||||||
private CancellationTokenSource _cancelSource;
|
private CancellationTokenSource _cancelSource;
|
||||||
private Task _task;
|
private Task _task;
|
||||||
|
|
||||||
public bool WasUnexpected => _wasUnexpected;
|
public bool WasUnexpected => _wasStopUnexpected;
|
||||||
private bool _wasUnexpected;
|
private bool _wasStopUnexpected;
|
||||||
|
|
||||||
public Exception Exception => _stopReason.SourceException;
|
public Exception Exception => _stopReason.SourceException;
|
||||||
private ExceptionDispatchInfo _stopReason;
|
private ExceptionDispatchInfo _stopReason;
|
||||||
@@ -53,7 +53,7 @@ namespace Discord
|
|||||||
continue; //Another thread sneaked in and started this manager before we got a lock, loop and try again
|
continue; //Another thread sneaked in and started this manager before we got a lock, loop and try again
|
||||||
|
|
||||||
_stopReason = null;
|
_stopReason = null;
|
||||||
_wasUnexpected = false;
|
_wasStopUnexpected = false;
|
||||||
|
|
||||||
Task[] tasksArray = tasks.ToArray();
|
Task[] tasksArray = tasks.ToArray();
|
||||||
Task<Task> anyTask = Task.WhenAny(tasksArray);
|
Task<Task> anyTask = Task.WhenAny(tasksArray);
|
||||||
@@ -74,8 +74,10 @@ namespace Discord
|
|||||||
await allTasks.ConfigureAwait(false);
|
await allTasks.ConfigureAwait(false);
|
||||||
|
|
||||||
//Run the cleanup function within our lock
|
//Run the cleanup function within our lock
|
||||||
await _stopAction().ConfigureAwait(false);
|
if (_stopAction != null)
|
||||||
|
await _stopAction().ConfigureAwait(false);
|
||||||
_task = null;
|
_task = null;
|
||||||
|
_cancelSource = null;
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -89,7 +91,8 @@ namespace Discord
|
|||||||
if (_task == null) return; //Are we running?
|
if (_task == null) return; //Are we running?
|
||||||
if (_cancelSource.IsCancellationRequested) return;
|
if (_cancelSource.IsCancellationRequested) return;
|
||||||
|
|
||||||
_cancelSource.Cancel();
|
if (_cancelSource != null)
|
||||||
|
_cancelSource.Cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Task Stop()
|
public Task Stop()
|
||||||
@@ -102,7 +105,8 @@ namespace Discord
|
|||||||
if (task == null) return TaskHelper.CompletedTask; //Are we running?
|
if (task == null) return TaskHelper.CompletedTask; //Are we running?
|
||||||
if (_cancelSource.IsCancellationRequested) return task;
|
if (_cancelSource.IsCancellationRequested) return task;
|
||||||
|
|
||||||
_cancelSource.Cancel();
|
if (_cancelSource != null)
|
||||||
|
_cancelSource.Cancel();
|
||||||
}
|
}
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
@@ -111,11 +115,12 @@ namespace Discord
|
|||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
if (_task == null) return; //Are we running?
|
if (_stopReason != null) return;
|
||||||
|
|
||||||
_stopReason = ExceptionDispatchInfo.Capture(ex);
|
_stopReason = ExceptionDispatchInfo.Capture(ex);
|
||||||
_wasUnexpected = isUnexpected;
|
_wasStopUnexpected = isUnexpected;
|
||||||
_cancelSource.Cancel();
|
if (_cancelSource != null)
|
||||||
|
_cancelSource.Cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Task Error(Exception ex, bool isUnexpected = true)
|
public Task Error(Exception ex, bool isUnexpected = true)
|
||||||
@@ -123,20 +128,22 @@ namespace Discord
|
|||||||
Task task;
|
Task task;
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
|
if (_stopReason != null) return TaskHelper.CompletedTask;
|
||||||
|
|
||||||
//Cache the task so we still have something to await if Cleanup is run really quickly
|
//Cache the task so we still have something to await if Cleanup is run really quickly
|
||||||
task = _task;
|
task = _task ?? TaskHelper.CompletedTask;
|
||||||
if (task == null) return TaskHelper.CompletedTask; //Are we running?
|
|
||||||
if (_cancelSource.IsCancellationRequested) return task;
|
if (_cancelSource.IsCancellationRequested) return task;
|
||||||
|
|
||||||
_stopReason = ExceptionDispatchInfo.Capture(ex);
|
_stopReason = ExceptionDispatchInfo.Capture(ex);
|
||||||
_wasUnexpected = isUnexpected;
|
_wasStopUnexpected = isUnexpected;
|
||||||
_cancelSource.Cancel();
|
if (_cancelSource != null)
|
||||||
|
_cancelSource.Cancel();
|
||||||
}
|
}
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Throws an exception if one was captured. </summary>
|
/// <summary> Throws an exception if one was captured. </summary>
|
||||||
public void Throw()
|
public void ThrowException()
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -144,5 +151,13 @@ namespace Discord
|
|||||||
_stopReason.Throw();
|
_stopReason.Throw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public void ClearException()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_stopReason = null;
|
||||||
|
_wasStopUnexpected = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ namespace Discord.Net.WebSockets
|
|||||||
_waitUntilConnect.Reset();
|
_waitUntilConnect.Reset();
|
||||||
_webSocket.Open();
|
_webSocket.Open();
|
||||||
_waitUntilConnect.Wait(cancelToken);
|
_waitUntilConnect.Wait(cancelToken);
|
||||||
|
_parent.TaskManager.ThrowException(); //In case our connection failed
|
||||||
return TaskHelper.CompletedTask;
|
return TaskHelper.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,8 +34,8 @@ namespace Discord.Net.WebSockets
|
|||||||
public string Host { get { return _host; } set { _host = value; } }
|
public string Host { get { return _host; } set { _host = value; } }
|
||||||
private string _host;
|
private string _host;
|
||||||
|
|
||||||
public ConnectionState State => (ConnectionState)_state;
|
public ConnectionState State => _state;
|
||||||
protected int _state;
|
protected ConnectionState _state;
|
||||||
|
|
||||||
public event EventHandler Connected;
|
public event EventHandler Connected;
|
||||||
private void RaiseConnected()
|
private void RaiseConnected()
|
||||||
@@ -104,8 +104,9 @@ namespace Discord.Net.WebSockets
|
|||||||
_lock.WaitOne();
|
_lock.WaitOne();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _taskManager.Stop().ConfigureAwait(false);
|
await _taskManager.Stop().ConfigureAwait(false);
|
||||||
_state = (int)ConnectionState.Connecting;
|
_taskManager.ClearException();
|
||||||
|
_state = ConnectionState.Connecting;
|
||||||
|
|
||||||
_cancelTokenSource = new CancellationTokenSource();
|
_cancelTokenSource = new CancellationTokenSource();
|
||||||
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, ParentCancelToken.Value).Token;
|
_cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelTokenSource.Token, ParentCancelToken.Value).Token;
|
||||||
@@ -122,13 +123,14 @@ namespace Discord.Net.WebSockets
|
|||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_taskManager.SignalError(ex, true);
|
_taskManager.SignalError(ex, true);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
protected void EndConnect()
|
protected void EndConnect()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_state = (int)ConnectionState.Connected;
|
_state = ConnectionState.Connected;
|
||||||
|
|
||||||
_connectedEvent.Set();
|
_connectedEvent.Set();
|
||||||
RaiseConnected();
|
RaiseConnected();
|
||||||
@@ -142,17 +144,17 @@ namespace Discord.Net.WebSockets
|
|||||||
protected abstract Task Run();
|
protected abstract Task Run();
|
||||||
protected virtual async Task Cleanup()
|
protected virtual async Task Cleanup()
|
||||||
{
|
{
|
||||||
await _engine.Disconnect().ConfigureAwait(false);
|
var oldState = _state;
|
||||||
|
_state = ConnectionState.Disconnecting;
|
||||||
|
|
||||||
|
await _engine.Disconnect().ConfigureAwait(false);
|
||||||
_cancelTokenSource = null;
|
_cancelTokenSource = null;
|
||||||
var oldState = _state;
|
|
||||||
_connectedEvent.Reset();
|
_connectedEvent.Reset();
|
||||||
|
|
||||||
if (oldState == (int)ConnectionState.Connected)
|
if (oldState == ConnectionState.Connected)
|
||||||
{
|
|
||||||
_state = (int)ConnectionState.Disconnected;
|
|
||||||
RaiseDisconnected(_taskManager.WasUnexpected, _taskManager.Exception);
|
RaiseDisconnected(_taskManager.WasUnexpected, _taskManager.Exception);
|
||||||
}
|
_state = ConnectionState.Disconnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Task ProcessMessage(string json)
|
protected virtual Task ProcessMessage(string json)
|
||||||
{
|
{
|
||||||
@@ -176,23 +178,18 @@ namespace Discord.Net.WebSockets
|
|||||||
{
|
{
|
||||||
while (!cancelToken.IsCancellationRequested)
|
while (!cancelToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
if (_state == (int)ConnectionState.Connected)
|
if (_state == ConnectionState.Connected)
|
||||||
{
|
{
|
||||||
SendHeartbeat();
|
SendHeartbeat();
|
||||||
await Task.Delay(_heartbeatInterval, cancelToken).ConfigureAwait(false);
|
await Task.Delay(_heartbeatInterval, cancelToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
await Task.Delay(100, cancelToken).ConfigureAwait(false);
|
await Task.Delay(1000, cancelToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException) { }
|
catch (OperationCanceledException) { }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
public abstract void SendHeartbeat();
|
public abstract void SendHeartbeat();
|
||||||
|
|
||||||
protected internal void ThrowError()
|
|
||||||
{
|
|
||||||
_taskManager.Throw();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user