Таймер для отслеживания острых сообщений 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), чем дает команду для повторного подключения к серверу.