定时器在c#中跟踪websocket尖锐消息

时间:2017-11-07 04:57:59

标签: c# websocket-sharp

我在Windows应用程序中使用websocket sharp dll从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(显示调用计时器时的当前时间)。

要求 - 我的计时器将检查是否错过了任何心跳消息,这是通过&#39; lastHeartbeatTime&#39;来完成的。存储接收心跳消息的时间的变量。

如果有人可以查看我的代码并建议我做错了什么或哪里,我将不胜感激。

2 个答案:

答案 0 :(得分:5)

已经给出了答案 - 您需要启动计时器,该计时器将在您收到消息的超时时间后触发,并在每次收到消息时重置该计时器。但似乎你想要代码示例,所以这里(带注释):

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();
    }

答案 1 :(得分:2)

从你的问题我理解的是,你的消息是在每秒后发送,但问题是当它停止你想要知道并再次运行它,如果是这样的话,你只需应用计时器并检查每一秒如果消息在一秒钟或更长时间后没有发送(check sentMessage()方法设置一个布尔值,如果发送消息,则应该给出true,否则为false),而不是给命令重新连接服务器。