using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; using TradeIdeas.ServerConnection; using TradeIdeas.MiscSupport; namespace TradeIdeas.TIQData { 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(); } } }