private void CreateSendRecieveChannels() { this.noneSendChannel = new SendChannel(SendOptions.None, sendDatagramsPool); this.inOrderSendChannel = new SendChannel(SendOptions.InOrder, sendDatagramsPool); this.reliableSendChannel = new SendChannel(SendOptions.Reliable, sendDatagramsPool); this.reliableInOrderSendChannel = new SendChannel(SendOptions.ReliableInOrder, sendDatagramsPool); this.noneReceiveChannel = new ReceiveChannel(SendOptions.None, this.localPeer, this); this.inOrderReceiveChannel = new ReceiveChannel(SendOptions.InOrder, this.localPeer, this); this.reliableReceiveChannel = new ReceiveChannel(SendOptions.Reliable, this.localPeer, this); this.reliableInOrderReceiveChannel = new ReceiveChannel(SendOptions.ReliableInOrder, this.localPeer, this); }
// returns true if caller should continue adding any additional packets in datagram internal bool TryAddReceivedPacket( ushort seq, SendOptions opts, PacketType type, byte[] buffer, int index, int payloadSize, bool isFirstPacketInDatagram) { // If we are not the keep alive master, i.e. this remote peer is, and this packet was // sent reliably reset ellpasedMilliseondsAtLastRealiablePacket[Received]. if (IsKeepAliveMaster && ((opts & SendOptions.Reliable) == SendOptions.Reliable)) { ellapasedSecondsSinceLastRealiablePacket = 0.0f; } switch (type) { case PacketType.Application: case PacketType.KeepAlive: case PacketType.AcceptJoin: { bool wasAppPacketAdded; ReceiveChannel channel = noneReceiveChannel; switch (opts) { case SendOptions.InOrder: channel = inOrderReceiveChannel; break; case SendOptions.Reliable: channel = reliableReceiveChannel; break; case SendOptions.ReliableInOrder: channel = reliableInOrderReceiveChannel; break; } if (!channel.TryAddReceivedPacket(seq, type, buffer, index, payloadSize, isFirstPacketInDatagram, out wasAppPacketAdded)) { return(false); } if (wasAppPacketAdded) { unreadPacketCount++; } return(true); } case PacketType.ACK: { // Look for the oldest datagram with the same seq AND channel type // seq is for which we ASSUME the ACK is for. int datagramIndex; Datagram sentDatagramAwaitingAck = null; for (datagramIndex = 0; datagramIndex < sentDatagramsAwaitingACK.Count; datagramIndex++) { Datagram datagram = sentDatagramsAwaitingACK[datagramIndex]; if (datagram.Sequence == seq && datagram.SendOptions == opts) { sentDatagramAwaitingAck = datagram; break; } } if (sentDatagramAwaitingAck == null) { // Possible reasons: // 1) ACK has arrived too late and the datagram must have already been removed. // 2) ACK duplicated and has already been processed // 3) ACK was unsolicited (i.e. malicious or buggy peer) // NOTE: If ACK "piggy-backed" on a reliable datagram that was re-sent // that datagram if recieved more than once would have been dropped // when processing the first Application packet so should never // get to here. localPeer.Log(LogLevel.Warning, "Datagram for ACK not found - too late?"); } else { // recieving our first ACK from this peer means they have Accepted us if (!hasAccepted) { hasAccepted = true; } // remove datagram sentDatagramsAwaitingACK.RemoveAt(datagramIndex); // Update QoS // ---------- // // If the datagram was not re-sent update latency, otherwise we do not // know which send this ACK is for so cannot determine latency. // // Also update re-sends sample if datagram was not re-sent with: not // re-sent. If was re-sent sample would have already been updated when // re-sent. if (sentDatagramAwaitingAck.ResentCount == 0) { TimeSpan rtt = localPeer.Stopwatch.Elapsed - sentDatagramAwaitingAck.EllapsedAtSent; qualityOfService.UpdateLatency(rtt); qualityOfService.UpdateResentSample(false); localPeer.Log(LogLevel.Debug, String.Format("ACK from: {0}, channel: {1}, seq: {2}, RTT: {3}s", PeerName, opts.ToString(), seq.ToString(), rtt.TotalSeconds.ToString())); localPeer.Log(LogLevel.Debug, String.Format("Latency now: {0}s", qualityOfService.RoudTripTime.TotalSeconds.ToString())); } else { localPeer.Log(LogLevel.Info, String.Format("ACK for re-sent datagram: {0}, channel: {1}, seq: {2}", PeerName, opts.ToString(), seq.ToString())); } // return datagram sendDatagramsPool.Return(sentDatagramAwaitingAck); } return(true); } case PacketType.Ping: { return(Pong()); } case PacketType.Pong: { if (localPeer.HasPingsAwaitingPong) { PingDetail detail = localPeer.PingsAwaitingPong.Find(pd => pd.PeerIdPingSentTo == Id); if (detail != null) { localPeer.RaisePongReceived(this, TimeSpan.FromSeconds(detail.EllapsedSeconds)); localPeer.RemovePingAwaitingPongDetail(detail); } } return(true); } case PacketType.JoinRequest: { if (hasAccepted) { // If peer has accepted must be is joining again (possible when did not // say Bye and has restarted again before timed out). Drop this // instance of peer and process Accept next update so both peers on // the same page, i.e. that we are starting a new connection. // byte[] datagram = new byte[Const.FALCON_PACKET_HEADER_SIZE + payloadSize]; FalconHelper.WriteFalconHeader(datagram, 0, PacketType.JoinRequest, SendOptions.None, (ushort)seq, (ushort)payloadSize); if (payloadSize > 0) { Buffer.BlockCopy(buffer, index, datagram, Const.FALCON_PACKET_HEADER_SIZE, payloadSize); } localPeer.RemovePeerOnNextUpdate(this); localPeer.EnqueuePacketToProcessOnNextUpdate(this.EndPoint, datagram); return(false); } return(Accept()); } case PacketType.DiscoverRequest: { DiscoverReply(); return(true); } case PacketType.DiscoverReply: { // do nothing, DiscoveryReply only relevant when peer not added return(true); } case PacketType.Bye: { localPeer.Log(LogLevel.Info, String.Format("Bye received from: {0}.", PeerName)); localPeer.RemovePeerOnNextUpdate(this); return(false); } default: { localPeer.Log(LogLevel.Error, String.Format("Datagram dropped - unexpected type: {0}, received from authenticated peer: {1}.", type, PeerName)); return(false); } } }