Browse Source

Fixed reconnect crash

tags/docs-0.9
RogueException 9 years ago
parent
commit
de6ffed5c9
3 changed files with 60 additions and 47 deletions
  1. +24
    -15
      src/Discord.Net/DiscordClient.cs
  2. +23
    -0
      src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs
  3. +13
    -32
      src/Discord.Net/Net/WebSockets/WebSocket.cs

+ 24
- 15
src/Discord.Net/DiscordClient.cs View File

@@ -95,7 +95,7 @@ namespace Discord
_api = new DiscordAPIClient(_config.LogLevel); _api = new DiscordAPIClient(_config.LogLevel);
_dataSocket = new DataWebSocket(this); _dataSocket = new DataWebSocket(this);
_dataSocket.Connected += (s, e) => { if (_state == (int)DiscordClientState.Connecting) CompleteConnect(); }; _dataSocket.Connected += (s, e) => { if (_state == (int)DiscordClientState.Connecting) CompleteConnect(); };
_dataSocket.Disconnected += async (s, e) => { RaiseDisconnected(e); if (e.WasUnexpected) await Connect(_token); /*await _dataSocket.Reconnect(_cancelToken);*/ };
_dataSocket.Disconnected += async (s, e) => { RaiseDisconnected(e); if (e.WasUnexpected) await Reconnect(_token); };
if (_config.EnableVoice) if (_config.EnableVoice)
{ {
_voiceSocket = new VoiceWebSocket(this); _voiceSocket = new VoiceWebSocket(this);
@@ -553,7 +553,8 @@ namespace Discord
/// <summary> Connects to the Discord server with the provided token. </summary> /// <summary> Connects to the Discord server with the provided token. </summary>
public async Task Connect(string token) public async Task Connect(string token)
{ {
await Disconnect().ConfigureAwait(false);
if (_state != (int)DiscordClientState.Disconnected)
await Disconnect().ConfigureAwait(false);


if (_config.LogLevel >= LogMessageSeverity.Verbose) if (_config.LogLevel >= LogMessageSeverity.Verbose)
RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, $"Using cached token."); RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, $"Using cached token.");
@@ -564,7 +565,8 @@ namespace Discord
/// <returns> Returns a token for future connections. </returns> /// <returns> Returns a token for future connections. </returns>
public async Task<string> Connect(string email, string password) public async Task<string> Connect(string email, string password)
{ {
await Disconnect().ConfigureAwait(false);
if (_state != (int)DiscordClientState.Disconnected)
await Disconnect().ConfigureAwait(false);
var response = await _api.Login(email, password).ConfigureAwait(false); var response = await _api.Login(email, password).ConfigureAwait(false);
if (_config.LogLevel >= LogMessageSeverity.Verbose) if (_config.LogLevel >= LogMessageSeverity.Verbose)
@@ -572,11 +574,15 @@ namespace Discord
return await ConnectInternal(response.Token).ConfigureAwait(false); return await ConnectInternal(response.Token).ConfigureAwait(false);
} }
private async Task<string> ConnectInternal(string token)
private Task Reconnect(string token)
{ {
if (_state != (int)DiscordClientState.Disconnected)
throw new InvalidOperationException("Client is already connected or connecting to the server.");
if (_config.LogLevel >= LogMessageSeverity.Verbose)
RaiseOnLog(LogMessageSeverity.Verbose, LogMessageSource.Authentication, $"Using cached token.");


return ConnectInternal(token);
}
private async Task<string> ConnectInternal(string token)
{
try try
{ {
_disconnectedEvent.Reset(); _disconnectedEvent.Reset();
@@ -618,14 +624,14 @@ namespace Discord
} }
protected void CompleteConnect() protected void CompleteConnect()
{ {
_state = (int)WebSocketState.Connected;
_state = (int)DiscordClientState.Connected;
_connectedEvent.Set(); _connectedEvent.Set();
RaiseConnected(); RaiseConnected();
} }


/// <summary> Disconnects from the Discord server, canceling any pending requests. </summary> /// <summary> Disconnects from the Discord server, canceling any pending requests. </summary>
public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false); public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false);
protected Task DisconnectInternal(Exception ex, bool isUnexpected = true, bool skipAwait = false)
protected Task DisconnectInternal(Exception ex = null, bool isUnexpected = true, bool skipAwait = false)
{ {
int oldState; int oldState;
bool hasWriterLock; bool hasWriterLock;
@@ -644,7 +650,7 @@ namespace Discord
if (hasWriterLock) if (hasWriterLock)
{ {
_wasDisconnectUnexpected = isUnexpected; _wasDisconnectUnexpected = isUnexpected;
_disconnectReason = ExceptionDispatchInfo.Capture(ex);
_disconnectReason = ex != null ? ExceptionDispatchInfo.Capture(ex) : null;
_cancelTokenSource.Cancel(); _cancelTokenSource.Cancel();
} }


@@ -662,12 +668,12 @@ namespace Discord
else else
task = _cancelToken.Wait(); task = _cancelToken.Wait();


try
{
await task.ConfigureAwait(false);
}
try { await task.ConfigureAwait(false); }
catch (Exception ex) { await DisconnectInternal(ex, skipAwait: true).ConfigureAwait(false); } catch (Exception ex) { await DisconnectInternal(ex, skipAwait: true).ConfigureAwait(false); }


//When the first task ends, make sure the rest do too
await DisconnectInternal(skipAwait: true);

bool wasUnexpected = _wasDisconnectUnexpected; bool wasUnexpected = _wasDisconnectUnexpected;
_wasDisconnectUnexpected = false; _wasDisconnectUnexpected = false;


@@ -680,8 +686,11 @@ namespace Discord
if (_config.EnableVoice) if (_config.EnableVoice)
await _voiceSocket.Disconnect().ConfigureAwait(false); await _voiceSocket.Disconnect().ConfigureAwait(false);


Message ignored;
while (_pendingMessages.TryDequeue(out ignored)) { }
if (_config.UseMessageQueue)
{
Message ignored;
while (_pendingMessages.TryDequeue(out ignored)) { }
}
_channels.Clear(); _channels.Clear();
_members.Clear(); _members.Clear();


+ 23
- 0
src/Discord.Net/Net/WebSockets/VoiceWebSocket.cs View File

@@ -70,6 +70,29 @@ namespace Discord.Net.WebSockets


await Connect(host, cancelToken); await Connect(host, cancelToken);
} }
public async Task Reconnect(CancellationToken cancelToken)
{
try
{
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false);
while (!cancelToken.IsCancellationRequested)
{
try
{
await Connect(_host, cancelToken).ConfigureAwait(false);
break;
}
catch (OperationCanceledException) { throw; }
catch (Exception ex)
{
RaiseOnLog(LogMessageSeverity.Error, $"DataSocket reconnect failed: {ex.GetBaseException().Message}");
//Net is down? We can keep trying to reconnect until the user runs Disconnect()
await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
}
}
}
catch (OperationCanceledException) { }
}


protected override Task[] Run() protected override Task[] Run()
{ {


+ 13
- 32
src/Discord.Net/Net/WebSockets/WebSocket.cs View File

@@ -69,29 +69,6 @@ namespace Discord.Net.WebSockets
}; };
} }


public async Task Reconnect(CancellationToken cancelToken)
{
try
{
await Task.Delay(_client.Config.ReconnectDelay, cancelToken).ConfigureAwait(false);
while (!cancelToken.IsCancellationRequested)
{
try
{
await Connect(_host, cancelToken).ConfigureAwait(false);
break;
}
catch (OperationCanceledException) { throw; }
catch (Exception ex)
{
RaiseOnLog(LogMessageSeverity.Error, $"DataSocket reconnect failed: {ex.GetBaseException().Message}");
//Net is down? We can keep trying to reconnect until the user runs Disconnect()
await Task.Delay(_client.Config.FailedReconnectDelay, cancelToken).ConfigureAwait(false);
}
}
}
catch (OperationCanceledException) { }
}
protected virtual async Task Connect(string host, CancellationToken cancelToken) protected virtual async Task Connect(string host, CancellationToken cancelToken)
{ {
if (_state != (int)WebSocketState.Disconnected) if (_state != (int)WebSocketState.Disconnected)
@@ -127,7 +104,7 @@ namespace Discord.Net.WebSockets
=> Connect(_host, _cancelToken);*/ => Connect(_host, _cancelToken);*/


public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false); public Task Disconnect() => DisconnectInternal(new Exception("Disconnect was requested by user."), isUnexpected: false);
protected Task DisconnectInternal(Exception ex, bool isUnexpected = true, bool skipAwait = false)
protected Task DisconnectInternal(Exception ex = null, bool isUnexpected = true, bool skipAwait = false)
{ {
int oldState; int oldState;
bool hasWriterLock; bool hasWriterLock;
@@ -146,7 +123,7 @@ namespace Discord.Net.WebSockets
if (hasWriterLock) if (hasWriterLock)
{ {
_wasDisconnectUnexpected = isUnexpected; _wasDisconnectUnexpected = isUnexpected;
_disconnectReason = ExceptionDispatchInfo.Capture(ex);
_disconnectReason = ex != null ? ExceptionDispatchInfo.Capture(ex) : null;
_cancelTokenSource.Cancel(); _cancelTokenSource.Cancel();
} }


@@ -158,13 +135,17 @@ namespace Discord.Net.WebSockets


protected virtual async Task RunTasks() protected virtual async Task RunTasks()
{ {
Task task = Task.WhenAll(Run());
Task[] tasks = Run();
Task firstTask = Task.WhenAny(tasks);
Task allTasks = Task.WhenAll(tasks);


try
{
await task.ConfigureAwait(false);
}
catch (Exception ex) { await DisconnectInternal(ex, skipAwait: true).ConfigureAwait(false); }
try { await firstTask.ConfigureAwait(false); }
catch (Exception ex) { await DisconnectInternal(ex: ex, skipAwait: true).ConfigureAwait(false); }

//When the first task ends, make sure the rest do too
await DisconnectInternal(skipAwait: true);
try { await allTasks.ConfigureAwait(false); }
catch { }


bool wasUnexpected = _wasDisconnectUnexpected; bool wasUnexpected = _wasDisconnectUnexpected;
_wasDisconnectUnexpected = false; _wasDisconnectUnexpected = false;
@@ -207,7 +188,7 @@ namespace Discord.Net.WebSockets
{ {
while (!cancelToken.IsCancellationRequested) while (!cancelToken.IsCancellationRequested)
{ {
if (_heartbeatInterval > 0)
if (_state == (int)WebSocketState.Connected)
{ {
QueueMessage(GetKeepAlive()); QueueMessage(GetKeepAlive());
await Task.Delay(_heartbeatInterval, cancelToken).ConfigureAwait(false); await Task.Delay(_heartbeatInterval, cancelToken).ConfigureAwait(false);


Loading…
Cancel
Save