// writes as many enqued as as can fit into datagram private void WriteEnquedAcksToDatagram(Datagram datagram, int index) { while (enqueudAcks.Count > 0 && (datagram.MaxSize - (index - datagram.Offset)) > Const.FALCON_PACKET_HEADER_SIZE) { AckDetail ack = enqueudAcks.Dequeue(); FalconHelper.WriteAck(ack, datagram.BackingBuffer, index); index += Const.FALCON_PACKET_HEADER_SIZE; } datagram.Resize(index - datagram.Offset); }
internal void EnqueueSend(PacketType type, Packet packet) { // NOTE: packet may be null in the case of Falcon system messages. if (packet != null && packet.BytesWritten > FalconPeer.MaxPayloadSize) { throw new InvalidOperationException(String.Format("Packet size: {0}, greater than max: {1}", packet.BytesWritten, FalconPeer.MaxPayloadSize)); } bool isFalconHeaderWritten = currentDatagramTotalBufferOffset > currentDatagram.Offset; if (isFalconHeaderWritten) { if (packet != null && (packet.BytesWritten + Const.ADDITIONAL_PACKET_HEADER_SIZE) > (currentDatagram.Count - (currentDatagramTotalBufferOffset - currentDatagram.Offset))) // i.e. cannot fit { // enqueue the current args and get a new one EnqueueCurrentDatagram(); isFalconHeaderWritten = false; } } if (!isFalconHeaderWritten) { // write the falcon header FalconHelper.WriteFalconHeader(currentDatagram.BackingBuffer, currentDatagram.Offset, type, channelType, seqCount, packet == null ? (ushort)0 : (ushort)packet.BytesWritten); currentDatagramTotalBufferOffset += Const.FALCON_PACKET_HEADER_SIZE; } else { // TODO limit max additional to 100 so receive channel can distinguish ordinal seq // write additional header FalconHelper.WriteAdditionalFalconHeader(currentDatagram.BackingBuffer, currentDatagramTotalBufferOffset, type, channelType, packet == null ? (ushort)0 : (ushort)packet.BytesWritten); currentDatagramTotalBufferOffset += Const.ADDITIONAL_PACKET_HEADER_SIZE; } if (packet != null) { //--------------------------------------------------------------------------------------------------- packet.CopyBytes(0, currentDatagram.BackingBuffer, currentDatagramTotalBufferOffset, packet.BytesWritten); //--------------------------------------------------------------------------------------------------- currentDatagramTotalBufferOffset += packet.BytesWritten; } }
// 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); } } }