コード例 #1
0
 private void SendIdentify(UpdateStatusParams initialPresence)
 {
     if (_sessionId is null) // IDENTITY
     {
         Send(new GatewayPayload
         {
             Operation = GatewayOperation.Identify,
             Data      = new IdentifyParams
             {
                 Compress       = false, // We don't want payload compression
                 LargeThreshold = 50,
                 Presence       = Optional.FromObject(initialPresence),
                 Properties     = new IdentityConnectionProperties
                 {
                     Os      = OsName,
                     Browser = LibraryName,
                     Device  = LibraryName
                 },
                 Shard = _shardId != null && _totalShards != null ? new int[] { _shardId.Value, _totalShards.Value } : Optional.Create <int[]>(),
                 Token = Authorization != null ? new Utf8String(Authorization.Parameter) : null
             }
         });
     }
     else // RESUME
     {
         Send(new GatewayPayload
         {
             Operation = GatewayOperation.Resume,
             Data      = new ResumeParams
             {
                 Sequence  = _lastSeq,
                 SessionId = _sessionId // TODO: Handle READY and get sessionId
             }
         });
     }
 }
コード例 #2
0
        public async Task RunAsync(string url, int?shardId = null, int?totalShards = null, UpdateStatusParams initialPresence = null)
        {
            Task exceptionSignal;
            await _stateLock.WaitAsync().ConfigureAwait(false);

            try
            {
                await StopAsyncInternal().ConfigureAwait(false);

                _url         = url;
                _shardId     = shardId;
                _totalShards = totalShards;
                _runCts      = new CancellationTokenSource();
                _sessionId   = null;

                _connectionTask = RunTaskAsync(initialPresence, _runCts.Token);
                exceptionSignal = _connectionTask;
            }
            finally
            {
                _stateLock.Release();
            }
            await exceptionSignal.ConfigureAwait(false);
        }
コード例 #3
0
        private async Task RunTaskAsync(UpdateStatusParams initialPresence, CancellationToken runCancelToken)
        {
            Task[] tasks         = null;
            bool   isRecoverable = true;
            int    backoffMillis = InitialBackoffMillis;
            var    jitter        = new Random();

            while (isRecoverable)
            {
                Exception disconnectEx  = null;
                var       connectionCts = new CancellationTokenSource();
                var       cancelToken   = CancellationTokenSource.CreateLinkedTokenSource(runCancelToken, connectionCts.Token).Token;
                using (var client = new ClientWebSocket())
                {
                    try
                    {
                        cancelToken.ThrowIfCancellationRequested();
                        var readySignal = new TaskCompletionSource <bool>();
                        _receivedData = true;

                        // Connect
                        State = ConnectionState.Connecting;
                        var uri = new Uri(_url + $"?v={ApiVersion}&encoding=etf&compress=zlib-stream"); // TODO: Enable
                        await client.ConnectAsync(uri, cancelToken).ConfigureAwait(false);

                        _zlibStream     = new DeflateStream(_compressed, CompressionMode.Decompress, true);
                        _readZlibHeader = false;

                        // Receive HELLO (timeout = ConnectionTimeoutMillis)
                        var receiveTask = ReceiveAsync(client, readySignal, cancelToken);
                        await WhenAny(new Task[] { receiveTask }, ConnectionTimeoutMillis,
                                      "Timed out waiting for HELLO").ConfigureAwait(false);

                        var evnt = await receiveTask.ConfigureAwait(false);

                        if (!(evnt.Data is HelloEvent helloEvent))
                        {
                            throw new Exception("First event was not a HELLO event");
                        }
                        int heartbeatRate = helloEvent.HeartbeatInterval;
                        ServerNames = helloEvent.Trace;

                        // Start tasks here since HELLO must be handled before another thread can send/receive
                        _sendQueue = new BlockingCollection <GatewayPayload>();
                        tasks      = new[]
                        {
                            RunSendAsync(client, cancelToken),
                            RunHeartbeatAsync(heartbeatRate, cancelToken),
                            RunReceiveAsync(client, readySignal, cancelToken)
                        };

                        // Send IDENTIFY/RESUME (timeout = IdentifyTimeoutMillis)
                        SendIdentify(initialPresence);
                        await WhenAny(tasks.Append(readySignal.Task), IdentifyTimeoutMillis,
                                      "Timed out waiting for READY or InvalidSession").ConfigureAwait(false);

                        if (await readySignal.Task.ConfigureAwait(false) == false)
                        {
                            continue; // Invalid session
                        }
                        // Success
                        _lastSeq      = 0;
                        backoffMillis = InitialBackoffMillis;
                        State         = ConnectionState.Connected;
                        Connected?.Invoke();

                        // Wait until an exception occurs (due to cancellation or failure)
                        await WhenAny(tasks).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        disconnectEx  = ex;
                        isRecoverable = IsRecoverable(ex);
                        if (!isRecoverable)
                        {
                            throw;
                        }
                    }
                    finally
                    {
                        var oldState = State;
                        State = ConnectionState.Disconnecting;

                        // Wait for the other tasks to complete
                        connectionCts.Cancel();
                        if (tasks != null)
                        {
                            try { await Task.WhenAll(tasks).ConfigureAwait(false); }
                            catch { } // We already captured the root exception
                        }

                        // receiveTask and sendTask must have completed before we can send/receive from a different thread
                        if (client.State == WebSocketState.Open)
                        {
                            try { await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None).ConfigureAwait(false); }
                            catch { } // We don't actually care if sending a close msg fails
                        }

                        _sendQueue  = null;
                        ServerNames = null;
                        State       = ConnectionState.Disconnected;
                        if (oldState == ConnectionState.Connected)
                        {
                            Disconnected?.Invoke(disconnectEx);
                        }
                    }
                    if (isRecoverable)
                    {
                        backoffMillis = (int)(backoffMillis * (BackoffMultiplier + (jitter.NextDouble() * BackoffJitter * 2.0 - BackoffJitter)));
                        if (backoffMillis > MaxBackoffMillis)
                        {
                            backoffMillis = MaxBackoffMillis;
                        }
                        await Task.Delay(backoffMillis).ConfigureAwait(false);
                    }
                }
            }
            _runCts.Cancel(); // Reset to initial canceled state
        }
コード例 #4
0
 public void Run(string url, int?shardId = null, int?totalShards = null, UpdateStatusParams initialPresence = null)
 => RunAsync(url, shardId, totalShards, initialPresence).ConfigureAwait(false).GetAwaiter().GetResult();