Exemplo n.º 1
0
        public async Task SubscribeAsync(bool reConnectOnDisconnect)
        {
            var uri = ExchangeClientBase.IsSandbox ? WSS_SANDBOX_ENDPOINT_URL : WSS_ENDPOINT_URL;

            if (_authContainer != null)             // authenticated feed
            {
                uri = new Uri(uri, "/users/self/verify");
            }
            cancellationTokenSource = new CancellationTokenSource();

            while (!cancellationTokenSource.IsCancellationRequested)
            {
                string disconnectReason = "";
                try
                {
                    webSocketClient = new ClientWebSocket();
                    await webSocketClient.ConnectAsync(uri, cancellationTokenSource.Token);

                    if (webSocketClient.State == System.Net.WebSockets.WebSocketState.Open && !cancellationTokenSource.IsCancellationRequested)
                    {
                        await rateGateRealtime.WaitToProceedAsync();                         // don't subscribe at too high of a rate
                        await sendSubscriptionMsgAsync(Products : Products, gdax_Channel : gdax_Channel);

                        // key is product name, value is whether connection was just opened
                        if (webSocketClient.State == System.Net.WebSockets.WebSocketState.Open && !cancellationTokenSource.IsCancellationRequested)
                        {                         // checking again bc maybe the server disconnected after the subscribe msg was sent
                                                  // + move to processing subscriptions section below later
                            foreach (var product in Products)
                            {
                                ConnectionOpened?.Invoke(product, gdax_Channel);
                            }
                        }
                        while (webSocketClient.State == System.Net.WebSockets.WebSocketState.Open && !cancellationTokenSource.IsCancellationRequested)
                        {
                            using (var timeoutCTS = new CancellationTokenSource(6500))                             // heartbeat every 1000 ms, so give it 5 hearbeat chances
                                using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutCTS.Token, cancellationTokenSource.Token))
                                    using (var stream = new MemoryStream(1024))
                                    {
                                        var  receiveBuffer = new ArraySegment <byte>(new byte[1024 * 8]);
                                        bool timedOut      = false;
                                        WebSocketReceiveResult webSocketReceiveResult;
                                        do
                                        {
                                            try
                                            {
                                                webSocketReceiveResult = await webSocketClient.ReceiveAsync(receiveBuffer, linkedTokenSource.Token);
                                            }
                                            catch (OperationCanceledException)
                                            {
                                                timedOut         = true;
                                                disconnectReason = " - stream timed out";
                                                break;
                                            }
                                            await stream.WriteAsync(receiveBuffer.Array, receiveBuffer.Offset, webSocketReceiveResult.Count, cancellationTokenSource.Token);
                                        } while (!webSocketReceiveResult.EndOfMessage && !cancellationTokenSource.IsCancellationRequested);

                                        if (!timedOut && !cancellationTokenSource.IsCancellationRequested)
                                        {
                                            var message       = stream.ToArray().Where(b => b != 0).ToArray();
                                            var messageString = Encoding.ASCII.GetString(message, 0, message.Length);
                                            if (!String.IsNullOrEmpty(messageString))
                                            {
                                                try
                                                {
                                                    var jToken = JToken.Parse(messageString);

                                                    var typeToken = jToken["type"];
                                                    if (typeToken == null)
                                                    {
                                                        RealtimeDataError?.Invoke(this, new RealtimeError("null typeToken: + " + Encoding.ASCII.GetString(message, 0, message.Length)));
                                                        return;                                         // go to next msg
                                                    }

                                                    var type = typeToken.Value <string>();
                                                    switch (type)
                                                    {
                                                    case "subscriptions":
                                                        // + process initial subscription confirmation
                                                        // + also for unsubscribe confirmation
                                                        break;

                                                    case "received":
                                                        var rr = new RealtimeReceived(jToken);
                                                        if (rr.Message != null)
                                                        {
                                                            RealtimeDataError?.Invoke(this, rr);
                                                        }
                                                        RealtimeReceived?.Invoke(this, rr);
                                                        break;

                                                    case "open":
                                                        var ro = new RealtimeOpen(jToken);
                                                        if (ro.Message != null)
                                                        {
                                                            RealtimeDataError?.Invoke(this, ro);
                                                        }
                                                        RealtimeOpen?.Invoke(this, ro);
                                                        break;

                                                    case "done":
                                                        var rd = new RealtimeDone(jToken);
                                                        if (rd.Message != null)
                                                        {
                                                            RealtimeDataError?.Invoke(this, rd);
                                                        }
                                                        RealtimeDone?.Invoke(this, rd);
                                                        break;

                                                    case "match":
                                                        var rm = new RealtimeMatch(jToken);
                                                        if (rm.Message != null)
                                                        {
                                                            RealtimeDataError?.Invoke(this, rm);
                                                        }
                                                        RealtimeMatch?.Invoke(this, rm);
                                                        break;

                                                    case "last_match":
                                                        var rlm = new RealtimeMatch(jToken);
                                                        if (rlm.Message != null)
                                                        {
                                                            RealtimeDataError?.Invoke(this, rlm);
                                                        }
                                                        RealtimeLastMatch?.Invoke(this, rlm);
                                                        break;

                                                    case "change":
                                                        var rc = new RealtimeChange(jToken);
                                                        if (rc.Message != null)
                                                        {
                                                            RealtimeDataError?.Invoke(this, rc);
                                                        }
                                                        RealtimeChange?.Invoke(this, rc);
                                                        break;

                                                    case "heartbeat":
                                                        // + should implement this (checking LastTraderId)
                                                        var hb = new Heartbeat(jToken);
                                                        Heartbeat?.Invoke(this, hb);
                                                        break;

                                                    case "error":
                                                        RealtimeDataError?.Invoke(this, new RealtimeError(jToken));
                                                        break;

                                                    default:
                                                        RealtimeDataError?.Invoke(this, new RealtimeError("Unexpected type: " + jToken));
                                                        break;
                                                    }
                                                }
                                                catch (JsonReaderException e)
                                                {
                                                    RealtimeDataError?.Invoke(this, new RealtimeError(
                                                                                  "JsonReaderException: " + e.Message + ":" + messageString));
                                                }
                                            }
                                            else
                                            {
                                                RealtimeDataError?.Invoke(this, new RealtimeError("empty message received. Connection state: "
                                                                                                  + webSocketClient.State + ", linkedToken: " + linkedTokenSource.Token.IsCancellationRequested));
                                            }
                                        }
                                    }
                        }
                    }
                }
                catch (Exception e)
                {
                    if (e.Message == "The remote party closed the WebSocket connection without completing the close handshake.")                     // System.Net.WebSockets.WebSocketException
                    {
                        disconnectReason = " - remote closed the WebSocket w/o completing the close handshake";
                    }
                    else if (e.Message == "Unable to connect to the remote server") // System.Net.WebSockets.WebSocketException
                    {
                        disconnectReason = " - unable to connect to server";        // shorten it a bit
                        await Task.Delay(10000);                                    // if unable to connect, then wait 10 seconds before trying to connect again
                    }
                    else
                    {
                        RealtimeStreamError?.Invoke(this, new RealtimeError("other exception caught: " + e.GetType() + " : " + e.Message));
                    }
                }
                if (!reConnectOnDisconnect)
                {
                    UnSubscribe();
                }
                foreach (var product in Products)
                {
                    RealtimeStreamError?.Invoke(this, new RealtimeError("disconnected" + disconnectReason));
                    ConnectionClosed?.Invoke(product, gdax_Channel);
                }
                if (!reConnectOnDisconnect)
                {
                    break;
                }
            }
        }