Ejemplo n.º 1
0
        /// <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;
            }
        }
Ejemplo n.º 2
0
        /// <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();
        }