void Receive_Notify(DhtClient client, LocationNotify notify) { if (notify.SignedLocation != null) { Store_Local(new DataReq(null, client.UserID, ServiceID, 0, notify.SignedLocation)); } ClientInfo info; if (!Clients.SafeTryGetValue(client.RoutingID, out info)) { return; } if (notify.GoingOffline) { Clients.SafeRemove(client.RoutingID); SignalUpdate(info, false); return; } info.LastSeen = Core.TimeNow; info.PingTimeout = notify.Timeout; info.NextPing = Core.TimeNow.AddSeconds(notify.Timeout); }
private void GoingOffline() { foreach (DhtClient client in NotifyUsers.Keys) { LocationNotify notify = new LocationNotify(); notify.Timeout = CurrentTimeout; notify.GoingOffline = true; Network.LightComm.SendReliable(client, ServiceID, 0, notify, true); } NotifyUsers.Clear(); }
void Receive_Ping(DhtClient client, LocationPing ping) { if (Core.User.Settings.Invisible) { return; } LocationNotify notify = new LocationNotify(); RecentPings.AddFirst(Core.TimeNow); while (RecentPings.Count > 30) { RecentPings.RemoveLast(); } // we want a target of 20 pings per minute ( 1 every 3 seconds) // pings per minute = RecentPings.count / (Core.TimeNow - RecentPings.Last).ToMinutes() float pingsPerMinute = (float)RecentPings.Count / (float)(Core.TimeNow - RecentPings.Last.Value).Minutes; notify.Timeout = (int)(60.0 * pingsPerMinute / 20.0); // 20 is target rate, so if we have 40ppm, multiplier is 2, timeout 120seconds notify.Timeout = Math.Max(60, notify.Timeout); // use 60 as lowest timeout CurrentTimeout = notify.Timeout; if (ping.RemoteVersion < LocalClient.Data.Version) { notify.SignedLocation = LocalClient.SignedData; } if (PendingNotifications.Contains(client)) { PendingNotifications.Remove(client); } //put node on interested list NotifyUsers[client] = Core.TimeNow.AddSeconds(notify.Timeout + 15); // *** small security concern, notifies are not signed so they could be forged // signing vs unsigning is 144 vs 7 bytes, the bandwidth benefits outweigh forging // someone's online status at the moment // byte[] unsigned = notify.Encode(Network.Protocol); // byte[] signed = SignedData.Encode(Network.Protocol, Core.User.Settings.KeyPair, notify); Network.LightComm.SendReliable(client, ServiceID, 0, notify); }
void LightComm_ReceiveData(DhtClient client, byte[] data) { G2Header root = new G2Header(data); if (G2Protocol.ReadPacket(root)) { if (root.Name == LocationPacket.Ping) { Receive_Ping(client, LocationPing.Decode(root)); } if (root.Name == LocationPacket.Notify) { Receive_Notify(client, LocationNotify.Decode(root)); } } }
public static LocationNotify Decode(G2Header root) { LocationNotify notify = new LocationNotify(); if (G2Protocol.ReadPayload(root)) { notify.SignedLocation = Utilities.ExtractBytes(root.Data, root.PayloadPos, root.PayloadSize); } G2Protocol.ResetPacket(root); G2Header child = new G2Header(root.Data); while (G2Protocol.ReadNextChild(root, child) == G2ReadResult.PACKET_GOOD) { if (child.Name == Packet_GoingOffline) { notify.GoingOffline = true; continue; } if (!G2Protocol.ReadPayload(child)) { continue; } switch (child.Name) { case Packet_Timeout: notify.Timeout = CompactNum.ToInt32(child.Data, child.PayloadPos, child.PayloadSize); break; } } return(notify); }
public static LocationNotify Decode(G2Header root) { LocationNotify notify = new LocationNotify(); if (G2Protocol.ReadPayload(root)) notify.SignedLocation = Utilities.ExtractBytes(root.Data, root.PayloadPos, root.PayloadSize); G2Protocol.ResetPacket(root); G2Header child = new G2Header(root.Data); while (G2Protocol.ReadNextChild(root, child) == G2ReadResult.PACKET_GOOD) { if (child.Name == Packet_GoingOffline) { notify.GoingOffline = true; continue; } if (!G2Protocol.ReadPayload(child)) continue; switch (child.Name) { case Packet_Timeout: notify.Timeout = CompactNum.ToInt32(child.Data, child.PayloadPos, child.PayloadSize); break; } } return notify; }
void Core_SecondTimer() { OpCore global = Core.Context.Lookup; // global publish - so others can find entry to the op if (Core.User.Settings.OpAccess != AccessType.Secret && global != null && global.Network.Responsive && (global.Firewall == FirewallType.Open || Network.UseLookupProxies) && Core.TimeNow > NextGlobalPublish) { PublishGlobal(); } if (!Network.Established) { return; } // run code below every quarter second if (Core.TimeNow.Second % 15 != 0) { return; } // keep local client from being pinged, or removed LocalClient.LastSeen = Core.TimeNow.AddMinutes(1); LocalClient.NextPing = Core.TimeNow.AddMinutes(1); // remove expired clients - either from not notifying us, or we've lost interest and stopped pinging Clients.LockWriting(delegate() { foreach (ClientInfo client in Clients.Values.Where(c => Core.TimeNow > c.Timeout).ToArray()) { Clients.Remove(client.RoutingID); SignalUpdate(client, false); } }); // get form services users that we should keep tabs on LocalPings.Clear(); KnowOnline.Invoke(LocalPings); // ping clients that we are locally caching, or we have interest in Clients.LockReading(delegate() { foreach (ClientInfo client in (from c in Clients.Values where Core.TimeNow > c.NextPing && (Network.Routing.InCacheArea(c.UserID) || LocalPings.Contains(c.UserID)) select c).ToArray()) { Send_Ping(client); } }); // remove users no longer interested in our upates foreach (DhtClient expired in (from id in NotifyUsers.Keys where Core.TimeNow > NotifyUsers[id] select id).ToArray()) { NotifyUsers.Remove(expired); if (PendingNotifications.Contains(expired)) { PendingNotifications.Remove(expired); } } // send 2 per second, if new update, start over again foreach (DhtClient client in PendingNotifications.Take(2).ToArray()) { LocationNotify notify = new LocationNotify(); notify.Timeout = CurrentTimeout; notify.SignedLocation = LocalClient.SignedData; Network.LightComm.SendReliable(client, ServiceID, 0, notify); PendingNotifications.Remove(client); } }