void AddClockSyncData(PeerInfo peerInfo, ClockSyncData newData) { peerInfo.ClockSyncDataByAge.Enqueue(newData); int newDataIndex = 0; // Sort by RTT is using BestRTT sampling rule. // Otherwise, just "sort" by recency by inserting at index = 0. if (ClockSyncSamplingRule == ClockSyncSamplingRule.BestRTTs) { // Add the new clock sync data into the RTT-sorted list. // We keep a persistent list instead of sorting on the fly, since that would be slow. for (newDataIndex = 0; newDataIndex < peerInfo.ClockSyncDataByImportance.Count; newDataIndex++) { if (peerInfo.ClockSyncDataByImportance[newDataIndex].RTT >= newData.RTT) { break; } } } peerInfo.ClockSyncDataByImportance.Insert(newDataIndex, newData); // Limit clock sync history. while (peerInfo.ClockSyncDataByAge.Count > _clockSyncHistorySize) { // Here is an awesome way to remove from the queue and the RTT-sorted list at the same time. peerInfo.ClockSyncDataByImportance.Remove(peerInfo.ClockSyncDataByAge.Dequeue()); } if (PeerUpdated != null) { PeerUpdated(peerInfo.Index, newData.RTT, (int)(peerInfo.BytesToTLS * _pingRate * 0.008), (int)(peerInfo.BytesFromTLS * _pingRate * 0.008)); } peerInfo.BytesToTLS = 0; peerInfo.BytesFromTLS = 0; }
/// <summary> /// Process a message from one of the peers /// </summary> /// <param name="peerIndex">Index number for the peer.</param> /// <param name="message">The message to be processed.</param> public void ProcessIncomingMessage(ushort peerIndex, TimelineMessage message) { lock (_peerLock) { lock (_timelineLock) { PeerInfo peerInfo = null; if (!_peersByIndex.TryGetValue(peerIndex, out peerInfo)) { return; } peerInfo.BytesToTLS += message.Data.Length; // Be sure to arrange the message types in descending order of frequency. // For example, sets are most frequent, so they are checked first. if (message.MessageType == TimelineMessageType.RelayAbsolute) { BinaryReader reader = new BinaryReader(new MemoryStream(message.Data)); // We only need to read the timeline index, since we are only relaying, not receiving. ushort timelineIndex = reader.ReadUInt16(); reader.Close(); // Get the timeline this message was intended for. TimelineInfo timelineInfo = null; if (!peerInfo.ConnectedTimelines.TryGetValue(timelineIndex, out timelineInfo)) { return; } // Add message to cache queue timelineInfo.CachedEntries.Add(message); if (TimelineSet != null) { TimelineSet(timelineIndex, timelineInfo.ID); } while (timelineInfo.CachedEntries.Count > timelineInfo.EntryCacheSize) { timelineInfo.CachedEntries.RemoveAt(0); } // Loop through all the subscribers except for the sender... foreach (var otherPeer in timelineInfo.ConnectedPeers) { if (peerInfo == otherPeer.Key) { continue; } // Create a new message to relay. TimelineMessage relayMessage = CreateRelayMessageFromTemplate(message, otherPeer.Value); otherPeer.Key.OutgoingMessages.Enqueue(relayMessage); } } else if (message.MessageType == TimelineMessageType.ClockSyncPong) { BinaryReader reader = new BinaryReader(new MemoryStream(message.Data)); double localPingTime = reader.ReadDouble(); double remotePongTime = reader.ReadDouble(); double localPongTime = (localPingTime + _now) / 2; reader.Close(); ClockSyncData newData = new ClockSyncData() { RTT = (float)(_now - localPingTime), Offset = (float)(localPongTime - remotePongTime) }; AddClockSyncData(peerInfo, newData); float correction = GetClockSyncCorrection(peerInfo); TimelineMessage correctionMessage = new TimelineMessage(TimelineMessageType.ClockSyncCorrection, BitConverter.GetBytes(correction), DeliveryMode.Unreliable); peerInfo.OutgoingMessages.Enqueue(correctionMessage); peerInfo.Pinging = false; } else if (message.MessageType == TimelineMessageType.ConnectTimeline) { // Message format: // 2 bytes (ushort) remote timeline index // byte array (byte[]) timeline id BinaryReader reader = new BinaryReader(new MemoryStream(message.Data)); ushort remoteTimelineIndex = reader.ReadUInt16(); byte[] timelineId = reader.ReadBytes(message.Data.Length - sizeof(ushort)); reader.Close(); ConnectPeerToTimeline(peerInfo, remoteTimelineIndex, timelineId); } else if (message.MessageType == TimelineMessageType.DisconnectTimeline) { // Call disconnect method BinaryReader reader = new BinaryReader(new MemoryStream(message.Data)); ushort remoteTimelineIndex = reader.ReadUInt16(); reader.Close(); TimelineInfo timelineInfo; if (peerInfo.ConnectedTimelines.TryGetValue(remoteTimelineIndex, out timelineInfo)) { DisconnectPeerFromTimeline(peerInfo, timelineInfo); } } else if (message.MessageType == TimelineMessageType.CacheSize) { BinaryReader reader = new BinaryReader(new MemoryStream(message.Data)); ushort remoteTimelineIndex = reader.ReadUInt16(); ushort cacheSize = reader.ReadUInt16(); reader.Close(); TimelineInfo timelineInfo; if (peerInfo.ConnectedTimelines.TryGetValue(remoteTimelineIndex, out timelineInfo)) { timelineInfo.EntryCacheSize = cacheSize; while (timelineInfo.CachedEntries.Count > cacheSize) { timelineInfo.CachedEntries.RemoveAt(0); } } } } } }