Таймер для отслеживания острых сообщений websocket в C#

Я использую websocket sharp dll в своем приложении для Windows, чтобы получать сообщения от сервера GDAX. Пока все работает нормально - сообщения приходят, и я их обрабатываю. Дело в том, что я застреваю, когда сообщения перестают приходить. По крайней мере, я не нахожу ничего в событии WebSocket.OnMessage ( https://github.com/sta/websocket-sharp), которое могло бы помочь мне в отслеживании, когда сообщения остановлены (я также пытался вызвать сообщения)

Теперь сообщения, которые я получил, имеют тип сообщения "Heartbeat", который отправляется каждую секунду. Я хочу добавить отдельный элемент управления таймером, чтобы проверить, приходят ли сообщения сердцебиения каждую секунду или нет, и если он перестает приходить, тогда мне нужно будет снова подключить сервер. Но так как ничего не происходит, когда сообщения перестают поступать, как я могу отследить это, где я должен поместить код таймера, чтобы проверить, когда сообщения сердцебиения перестают поступать?

Я надеюсь, что смогу объяснить ситуацию, в которой я поражен. Если кто-то хочет мне помочь и ему нужно больше информации, пожалуйста, дайте мне знать.

Обновить

    private void _3_Load(object sender, EventArgs e)
    {          
        ConnectAndGetWebsocketFeedMessages();           
    }

    public delegate void WSOpen(string text);
    public delegate void WSMessage(string message);
    public delegate void WSError(string text);
    public delegate void WSClose(string text);

    private static string _endPoint = "wss://ws-feed.gdax.com";
    WebSocket ws = new WebSocket(_endPoint);

    private bool IsConnected { get; set; }
    private string ProductId { get; set; }

    string productId = "LTC-EUR";
    ConcurrentQueue<string> concurrentQueue = new ConcurrentQueue<string>();

    public void SetWebSocketSharpEvents()
    {
        ws.Log.Level = LogLevel.Trace;

        ws.OnOpen += (sender, e) =>
        {
            IsConnected = true;
            OnWSOpen("Connection Status :: Connected *********");
        };
        ws.EmitOnPing = true;
        ws.OnMessage += (sender, e) =>
        {
            if (e.IsPing)
            {
                OnWSMessage("ping received");
            }
            else
            {                    
                OnWSMessage(e.Data);
            }
        };

        ws.OnError += (sender, e) =>
        {
            IsConnected = false;
            OnWSError(e.Message); //An exception has occurred during an OnMessage event. An error has occurred in closing the connection.
            if (ws.IsAlive)
                ws.Close();
        };

        ws.OnClose += (sender, e) =>
        {
            IsConnected = false;
            OnWSClose("Close");
        };

        ws.ConnectAsync();
    }

    private void ConnectAndGetWebsocketFeedMessages()
    {            
        SetWebSocketSharpEvents();
    }

    private void SubscribeProduct(string sProductID)
    {
        if (IsConnected)
        {
            ProductId = sProductID;
            string data = "{\"type\": \"subscribe\", \"product_ids\": [\"" + sProductID + "\"]}";
            ws.Send(data);
            ws.Send("{\"type\": \"heartbeat\", \"on\": true}");
        }
    }

    void OnWSOpen(string text)
    {
        SubscribeProduct(productId);
        timer1.Interval = 1000;
        timer1.Tick += timer1_Tick;
        timer1.Start();
    }

    DateTime lastHeartbeatTime = DateTime.MinValue;
    bool isTimerStart = false;
    void OnWSMessage(string message)
    {
        concurrentQueue.Enqueue(message);
        SaveHeartbeatMessageTime(message);
        ProcessMessage(message);
    }

    private void SaveHeartbeatMessageTime(string jsonString)
    {
        var jToken = JToken.Parse(jsonString);

        var typeToken = jToken["type"];

        var type = typeToken.ToString();

        if (type == "heartbeat")
        {
            lastHeartbeatTime = DateTime.Now;
            this.Invoke(new MethodInvoker(delegate()
            {
                lbllastheartbeat.Text = lastHeartbeatTime.ToLongTimeString();
            }));               
        }
    }

    private void ProcessMessage(string message) {  }

    void OnWSError(string text) { }

    void OnWSClose(string text) { }

    bool isMessagesReceived = false;

    private void timer1_Tick(object sender, EventArgs e) // it stops working as soon as lbllastheartbeat gets some value
    {
        DateTime currentTime = DateTime.Now;
        TimeSpan duration = currentTime.Subtract(lastHeartbeatTime);
        this.Invoke(new MethodInvoker(delegate()
        {
            lblNow.Text = currentTime.ToLongTimeString();
        }));
        if (Int16.Parse(duration.ToString("ss")) > 1)
        {
            // reconnect here
        }
    }

Редактировать Я использую элемент управления таймера формы Windows, и он продолжает вызывать метод timer1_Tick и не вызывает метод OnWSMessage. Как я могу гарантировать, что оба будут работать параллельно, и если какое-либо сообщение будет пропущено или сообщение перестанет приходить, оно снова подключится?

Edit2 Решения, представленные ниже, предлагают добавить функцию таймера в событие onMessage, но что произойдет, если я не получу сообщения? Если сообщения не получены, то код ничего не делает. Я взял глобальную переменную, и всякий раз, когда приходит сообщение, он добавляет время в этой переменной. Теперь я хочу запустить отдельный элемент управления таймером, который будет проверять, есть ли что-либо в этой переменной, и если ее значение, то есть разница секунд больше 1, тогда продолжайте проверять что-то еще.

Есть ли кто-нибудь, кто может разобраться в этом и посоветовать, пожалуйста.

Update2: я все еще хочу сделать это с помощью элемента управления windows.timer, а не threading.timer. Я взял две метки в своем приложении для Windows: lbllastheartbeat (чтобы показать время получения сообщения пульса) и lblNow (чтобы показать текущее время при вызове таймера).

Требование - Мой таймер проверит, пропущено ли какое-либо сообщение сердцебиения, и это делается с помощью переменной lastHeartbeatTime, в которой хранится время получения сообщения сердцебиения.

Буду признателен, если кто-нибудь сможет просмотреть мой код и предложить, что или где я делаю не так.

2 ответа

Ответ уже дан - вам нужно запустить таймер, который будет срабатывать по истечении вашего времени ожидания при получении сообщения, и сбрасывать этот таймер каждый раз, когда вы получаете сообщение. Но, кажется, вы хотите пример кода, так что вот (с комментариями):

System.Threading.Timer _timeoutTimer;
private readonly object _timeoutTimerLock = new object();
private void ResetTimeoutTimer() {
    // if you are sure you will never access this from multiple threads at the same time - remove lock
    lock (_timeoutTimerLock) {
        // initialize or reset the timer to fire once, after 2 seconds
        if (_timeoutTimer == null)
            _timeoutTimer = new System.Threading.Timer(ReconnectAfterTimeout, null, TimeSpan.FromSeconds(2), Timeout.InfiniteTimeSpan);
        else
            _timeoutTimer.Change(TimeSpan.FromSeconds(2), Timeout.InfiniteTimeSpan);
    }
}

private void StopTimeoutTimer() {
    // if you are sure you will never access this from multiple threads at the same time - remove lock
    lock (_timeoutTimerLock) {
        if (_timeoutTimer != null)
            _timeoutTimer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
    }
}

private void ReconnectAfterTimeout(object state) {
    // reconnect here
}

public void SetWebSocketSharpEvents() {
    ws.Log.Level = LogLevel.Trace;

    ws.OnOpen += (sender, e) => {
        // start timer here so that if you don't get first message after 2 seconds - reconnect
        ResetTimeoutTimer();
        IsConnected = true;
        OnWSOpen("Connection Status :: Connected *********");
    };
    ws.EmitOnPing = true;
    ws.OnMessage += (sender, e) => {
        // and here
        ResetTimeoutTimer();
        if (e.IsPing) {
            OnWSMessage("ping received");
        }
        else {
            OnWSMessage(e.Data);
        }
    };

    ws.OnError += (sender, e) => {
        // stop it here
        StopTimeoutTimer();
        IsConnected = false;
        OnWSError(e.Message); //An exception has occurred during an OnMessage event. An error has occurred in closing the connection.


  if (ws.IsAlive)
                ws.Close();
        };

        ws.OnClose += (sender, e) => {
            // and here
            StopTimeoutTimer();
            IsConnected = false;
            OnWSClose("Close");
        };

        ws.ConnectAsync();
    }

Исходя из вашего вопроса, я понимаю, что ваше сообщение отправляется через каждую секунду, но проблема заключается только в том, что когда он останавливается, вы хотите узнать и запустить его снова, если это так, вы просто применяете таймер и проверяете каждую секунду, если сообщение не отправлено через секунду или более (проверьте, что метод sentMessage() устанавливает логическое значение, если отправленное сообщение должно давать true, в противном случае false), чем дает команду для повторного подключения к серверу.

Другие вопросы по тегам