/// <summary> /// Handles sending packets to the target endpoint. /// </summary> private void PerformSend() { this.m_SendAccumulator += this.m_DeltaTime; while (this.m_SendAccumulator >= this.GetSendTime()) { var queues = new[] { new KeyValuePair <uint, Queue <byte[]> >(MxMessage.RealtimeProtocol, this.m_PendingRealtimeSendPackets), new KeyValuePair <uint, Queue <byte[]> >(MxMessage.ReliableProtocol, this.m_PendingReliableSendPackets) }; foreach (var item in queues) { var protocol = item.Key; var queue = item.Value; byte[][] packets; if (protocol == MxMessage.ReliableProtocol) { // In reliable mode, we know the sender is MxReliability and that it's optimized it's // send calls for ~512 bytes. Thus we just take one packet and use that. packets = queue.Count > 0 ? new[] { queue.Peek() } : new byte[0][]; } else { // In real time mode, we use all of the currently queued packets and hope that the resulting // size is not larger than 512 bytes (or is otherwise fragmented and dropped along the way). packets = queue.ToArray(); } using (var memory = new MemoryStream()) { var message = new MxMessage { ProtocolID = protocol, Payloads = packets.Select(x => new MxPayload { Data = x }).ToArray(), Sequence = this.m_LocalSequenceNumber, Ack = this.m_RemoteSequenceNumber }; message.SetAckBitfield(this.m_ReceiveQueue.ToArray()); var serializer = new MxMessageSerializer(); serializer.Serialize(memory, message); var len = (int)memory.Position; memory.Seek(0, SeekOrigin.Begin); var bytes = new byte[len]; memory.Read(bytes, 0, len); if (len > 512 && protocol != MxMessage.ReliableProtocol) { // TODO: Probably fire an event here to warn that the queued messages exceeds the safe packet size. } try { try { this.m_SharedUdpClient.Send(bytes, bytes.Length, this.m_TargetEndPoint); this.m_SendQueue.Add(this.m_LocalSequenceNumber, this.GetUnixTimestamp()); this.m_SendMessageQueue.Add(this.m_LocalSequenceNumber, new KeyValuePair <uint, byte[][]>(protocol, packets)); this.m_LocalSequenceNumber++; if (protocol == MxMessage.ReliableProtocol) { // Only dequeue the pending send packet once we know that it's at least // left this machine successfully (otherwise there'd be no message lost // event if they got consumed by a SocketException). if (queue.Count > 0) { queue.Dequeue(); } } else { // Only clear the pending send packets once we know that they've at least // left this machine successfully (otherwise there'd be no message lost // event if they got consumed by a SocketException). queue.Clear(); } // Raise the OnMessageSent event. foreach (var packet in packets) { this.OnMessageSent(new MxMessageEventArgs { Client = this, Payload = packet }); } } catch (SocketException) { // We don't care. } } catch (ObjectDisposedException) { // We don't care. } } } this.m_SendAccumulator -= this.m_DeltaTime; } }
/// <summary> /// Handles the packets currently queued in the receive queue. /// </summary> private void PerformReceive() { if (this.m_ReceivedPackets.Count == 0) { this.m_DisconnectAccumulator++; return; } foreach (var packet in this.m_ReceivedPackets) { this.m_DisconnectAccumulator = 0; using (var memory = new MemoryStream(packet)) { var serializer = new MxMessageSerializer(); var message = (MxMessage)serializer.Deserialize(memory, null, typeof(MxMessage)); if (message.Payloads == null) { message.Payloads = new MxPayload[0]; } foreach (var payload in message.Payloads.Where(payload => payload.Data == null)) { payload.Data = new byte[0]; } var difference = MxUtility.GetSequenceNumberDifference( message.Sequence, this.m_RemoteSequenceNumber); if (difference > 0) { // Calculate the difference between the old // sequence number and the new sequence number // we need to push "false" into the queue for // the missing messages. for (var i = 0; i < difference - 1; i++) { this.PushIntoQueue(this.m_ReceiveQueue, false); } // We push the "true" value for this message after // firing the OnReceived event (so we can not acknowledge // it if the event callbacks do not want us to). // Check based on items in the queue. foreach (var kv in this.m_SendQueue.ToArray()) { var idx = kv.Key; if (!message.HasAck(idx)) { // We aren't acking this message yet. continue; } if (message.DidAck(idx)) { // We already checked for existance of the key above. var sendTimestamp = this.m_SendQueue[idx]; var rtt = this.GetUnixTimestamp() - sendTimestamp; this.PushIntoQueue(this.m_RTTQueue, rtt); var payloads = this.m_SendMessageQueue[idx]; this.m_SendQueue.Remove(idx); this.m_SendMessageQueue.Remove(idx); this.Latency = rtt; foreach (var payload in payloads.Value) { this.OnMessageAcknowledged( new MxMessageEventArgs { Client = this, Payload = payload, ProtocolID = payloads.Key }); } } else { this.HandleLostMessage(idx); } } foreach (var kv in this.m_SendQueue.ToArray()) { var idx = kv.Key; if (MxUtility.GetSequenceNumberDifference(message.Ack - MxUtility.UIntBitsize, idx) > 0) { this.HandleLostMessage(idx); } } this.m_RemoteSequenceNumber = message.Sequence; var doNotAcknowledge = false; foreach (var payload in message.Payloads) { var eventArgs = new MxMessageReceiveEventArgs { Client = this, Payload = payload.Data, DoNotAcknowledge = doNotAcknowledge, ProtocolID = message.ProtocolID }; this.OnMessageReceived(eventArgs); doNotAcknowledge = eventArgs.DoNotAcknowledge; } if (!doNotAcknowledge) { this.PushIntoQueue(this.m_ReceiveQueue, true); } } } } this.m_ReceivedPackets.Clear(); }