using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using TradeIdeas.ServerConnection;
using TradeIdeas.MiscSupport;
namespace TradeIdeas.TIProData
{
public delegate void PingUpdate(TimeSpan pingTime);
public class PingManager
{ // Pings do a few things. They keep the connection open; the server would cut us off
// if it doesn't hear from us in a while. They allow us to report the connection
// latency to the user. And sometimes the O/S can detect a TCP/IP failure sooner if
// you try to send data rather than just waiting to receive data.
///
/// This happens after each *successful* ping response.
///
public event PingUpdate PingUpdate;
DateTime _lastSendTime;
DateTime _nextSendTime;
DateTime _cutOffTime;
Stopwatch _stopwatch = new Stopwatch();
private void ResetCutOffTime()
{
_cutOffTime = DateTime.Now.AddSeconds(20);
}
private void SendNow()
{
TalkWithServer serverConnection = _serverConnection;
// Note that _serverConnection is set in a different thread, so it might
// change while this method is running. Saving a copy of it should be
// sufficient to avoid any problems. It's always okay to send to an old
// server connection, by design. The bigger issue is avoiding a null
// pointer exception.
if (null == serverConnection)
// This happens sometimes! It's a race condition.
return;
_lastSendTime = DateTime.Now;
_nextSendTime = _lastSendTime.AddSeconds(5);
_stopwatch.Reset();
_stopwatch.Start();
Dictionary message =
TalkWithServer.CreateMessage("command", "ping", "response", "1");
serverConnection.SendMessage(message, PingResponse);
}
private void PingResponse(byte[] body, object unused)
{
if (body != null)
{
_stopwatch.Stop();
// convert to long so we can do division
long TotalTicks = _stopwatch.Elapsed.Ticks;
TimeSpan oneWay = TimeSpan.FromTicks(TotalTicks / 2);
ResetCutOffTime();
_connectionLifeCycle.SetStatus("Working: " + ServerFormats.Now.ToString());
PingUpdate callback = PingUpdate;
if (callback != null)
try
{
// report the one way ping time to account for the fact that the data
// comes as a push.
callback(oneWay);
}
catch
{
//keep working
}
}
}
private TalkWithServer _serverConnection;
private void OnConnect(ConnectionLifeCycle sender, TalkWithServer serverConnection)
{
if (sender.Status != ConnectionLifeCycle.ConnectionStatus.Full)
return;
_serverConnection = serverConnection;
ResetCutOffTime();
SendNow();
}
private void OnTimer(ConnectionLifeCycle sender)
{
if (sender.Status != ConnectionLifeCycle.ConnectionStatus.Full)
return;
DateTime now = DateTime.Now;
if (now >= _cutOffTime)
{
_connectionLifeCycle.SoftReset();
ResetCutOffTime();
}
else if (now >= _nextSendTime)
SendNow();
}
private ConnectionLifeCycle _connectionLifeCycle;
internal PingManager(ConnectionLifeCycle connectionLifeCycle)
{
_connectionLifeCycle = connectionLifeCycle;
_connectionLifeCycle.OnConnection += OnConnect;
_connectionLifeCycle.OnTimer += OnTimer;
ResetCutOffTime();
}
}
}