/// <summary> /// Reads a 32 bit floating point value written using Write(Single) /// </summary> public float ReadSingle() { NetException.Assert(m_bitLength - m_readPosition >= 32, c_readOverflowError); if ((m_readPosition & 7) == 0) // read directly { float retval = BitConverter.ToSingle(m_data, m_readPosition >> 3); m_readPosition += 32; return(retval); } byte[] bytes = (byte[])Interlocked.Exchange(ref s_buffer, null) ?? new byte[c_bufferSize]; ReadBytes(bytes, 0, 4); float res = BitConverter.ToSingle(bytes, 0); s_buffer = bytes; return(res); }
public static uint ReadVariableUInt32(byte[] buffer, ref int offset) { int num1 = 0; int num2 = 0; while (true) { NetException.Assert(num2 != 0x23, "Bad 7-bit encoded integer"); byte num3 = buffer[offset++]; num1 |= (num3 & 0x7f) << (num2 & 0x1f); num2 += 7; if ((num3 & 0x80) == 0) { return((uint)num1); } } }
private void ExecuteSend(float now, NetOutgoingMessage message) { int seqNr = m_sendStart; m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers; m_connection.QueueSendMessage(message, seqNr); int storeIndex = seqNr % m_windowSize; NetException.Assert(m_storedMessages[storeIndex].Message == null); m_storedMessages[storeIndex].NumSent++; m_storedMessages[storeIndex].Message = message; m_storedMessages[storeIndex].LastSent = now; return; }
/// <summary> /// Creates a new message for sending /// </summary> /// <param name="initialCapacity">initial capacity in bytes</param> public NetOutgoingMessage CreateMessage(int initialCapacity) { NetOutgoingMessage retval; if (m_outgoingMessagesPool == null || !m_outgoingMessagesPool.TryDequeue(out retval)) { retval = new NetOutgoingMessage(); } NetException.Assert(retval.m_recyclingCount == 0, "Wrong recycling count! Should be zero" + retval.m_recyclingCount); if (initialCapacity > 0) { retval.m_data = GetStorage(initialCapacity); } return(retval); }
/// <summary> /// Reads the specified number of bits into a preallocated array /// </summary> /// <param name="into">The destination array</param> /// <param name="offset">The offset where to start writing in the destination array</param> /// <param name="numberOfBits">The number of bits to read</param> public void ReadBits(byte[] into, int offset, int numberOfBits) { NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); NetException.Assert(offset + NetUtility.BytesToHoldBits(numberOfBits) <= into.Length); int numberOfWholeBytes = numberOfBits / 8; int extraBits = numberOfBits - (numberOfWholeBytes * 8); NetBitWriter.ReadBytes(m_data, numberOfWholeBytes, m_readPosition, into, offset); m_readPosition += (8 * numberOfWholeBytes); if (extraBits > 0) { into[offset + numberOfWholeBytes] = ReadByte(extraBits); } return; }
public UInt64 PeekUInt64(int numberOfBits) { NetException.Assert((numberOfBits > 0 && numberOfBits <= 64), "ReadUInt() can only read between 1 and 64 bits"); NetException.Assert(m_bitLength - m_readPosition >= numberOfBits, c_readOverflowError); ulong retval; if (numberOfBits <= 32) { retval = NetBitWriter.ReadUInt32(m_data, numberOfBits, m_readPosition); } else { retval = NetBitWriter.ReadUInt32(m_data, 32, m_readPosition); retval |= (UInt64)NetBitWriter.ReadUInt32(m_data, numberOfBits - 32, m_readPosition + 32) << 32; } return(retval); }
/// <summary> /// Recycle the message to the library for reuse /// </summary> internal void Recycle(NetOutgoingMessage msg) { VerifyNetworkThread(); #if DEBUG lock (m_connections) { foreach (NetConnection conn in m_connections) { for (int i = 0; i < conn.m_unsentMessages.Count; i++) { NetSending send = conn.m_unsentMessages.TryPeek(i); if (send != null && send.Message == msg) { throw new NetException("Ouch! Recycling unsent message!"); } foreach (NetSending asend in conn.m_unackedSends) { if (asend.Message == msg) { throw new NetException("Ouch! Recycling stored message!"); } } } } } #endif NetException.Assert(msg.m_numUnfinishedSendings == 0, "Recycling m_numUnfinishedSendings is " + msg.m_numUnfinishedSendings + " (expected 0)"); if (msg.m_data != null) { lock (m_storagePool) { if (!m_storagePool.Contains(msg.m_data)) { m_storedBytes += msg.m_data.Length; m_storagePool.Add(msg.m_data); } } msg.m_data = null; } m_outgoingMessagesPool.Enqueue(msg); }
/// <summary> /// Reads a 64 bit floating point value written using Write(Double) /// </summary> public double ReadDouble() { NetException.Assert(m_bitLength - m_readPosition >= 64, c_readOverflowError); if ((m_readPosition & 7) == 0) // read directly { // read directly double retval = BitConverter.ToDouble(m_data, m_readPosition >> 3); m_readPosition += 64; return(retval); } byte[] bytes = (byte[])Interlocked.Exchange(ref s_buffer, null) ?? new byte[c_bufferSize]; ReadBytes(bytes, 0, 8); double res = BitConverter.ToDouble(bytes, 0); s_buffer = bytes; return(res); }
/// <summary> /// Send a message to a specific connection /// </summary> /// <param name="msg">The message to send</param> /// <param name="recipient">The recipient connection</param> /// <param name="method">How to deliver the message</param> /// <param name="sequenceChannel">Sequence channel within the delivery method</param> public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel) { if (msg == null) { throw new ArgumentNullException("msg"); } if (recipient == null) { throw new ArgumentNullException("recipient"); } if (sequenceChannel >= NetConstants.NetChannelsPerDeliveryMethod) { throw new ArgumentOutOfRangeException("sequenceChannel"); } NetException.Assert( ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) || ((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)), "Delivery method " + method + " cannot use sequence channels other than 0!" ); NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!"); if (msg.m_isSent) { throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); } msg.m_isSent = true; int len = NetConstants.UnfragmentedMessageHeaderSize + msg.LengthBytes; // headers + length, faster than calling msg.GetEncodedSize if (len <= recipient.m_currentMTU) { Interlocked.Increment(ref msg.m_recyclingCount); return(recipient.EnqueueMessage(msg, method, sequenceChannel)); } else { // message must be fragmented! SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel); return(NetSendResult.Queued); // could be different for each connection; Queued is "most true" } }
internal static int GetMTU(IList <NetConnection> recipients) { int count = recipients.Count; NetException.Assert(count > 0); int mtu = int.MaxValue; for (int i = 0; i < count; i++) { var conn = recipients[i]; int cmtu = conn.m_currentMTU; if (cmtu < mtu) { mtu = cmtu; } } return(mtu); }
/// <summary> /// Recycles a NetIncomingMessage instance for reuse; taking pressure off the garbage collector /// </summary> public void Recycle(NetIncomingMessage msg) { if (m_incomingMessagesPool == null || msg == null) { return; } NetException.Assert(m_incomingMessagesPool.Contains(msg) == false, "Recyling already recycled incoming message! Thread race?"); byte[] storage = msg.m_data; msg.m_data = null; Recycle(storage); msg.Reset(); if (m_incomingMessagesPool.Count < m_maxCacheCount) { m_incomingMessagesPool.Enqueue(msg); } }
private NetReceiverChannelBase CreateReceiverChannel(NetMessageType tp) { m_peer.VerifyNetworkThread(); // create receiver channel NetReceiverChannelBase chan; NetDeliveryMethod method = NetUtility.GetDeliveryMethod(tp); switch (method) { case NetDeliveryMethod.Unreliable: chan = new NetUnreliableUnorderedReceiver(this); break; case NetDeliveryMethod.ReliableOrdered: chan = new NetReliableOrderedReceiver(this, NetConstants.ReliableOrderedWindowSize); break; case NetDeliveryMethod.UnreliableSequenced: chan = new NetUnreliableSequencedReceiver(this); break; case NetDeliveryMethod.ReliableUnordered: chan = new NetReliableUnorderedReceiver(this, NetConstants.ReliableOrderedWindowSize); break; case NetDeliveryMethod.ReliableSequenced: chan = new NetReliableSequencedReceiver(this, NetConstants.ReliableSequencedWindowSize); break; case NetDeliveryMethod.Unknown: default: throw new NetException("Unhandled NetDeliveryMethod!"); } int channelSlot = (int)tp - 1; NetException.Assert(m_receiveChannels[channelSlot] == null); m_receiveChannels[channelSlot] = chan; return(chan); }
internal void ReleaseMessage(NetIncomingMessage msg) { NetException.Assert(msg.m_incomingMessageType != NetIncomingMessageType.Error); if (msg.m_isFragment) { HandleReleasedFragment(msg); return; } if (msg.SenderConnection != null && msg.SenderConnection.MessageHandler != null) { var handled = msg.SenderConnection.MessageHandler(msg); if (handled) { return; } } m_releasedIncomingMessages.Enqueue(msg); if (m_messageReceivedEvent != null) { m_messageReceivedEvent.Set(); } if (m_receiveCallbacks != null) { foreach (var tuple in m_receiveCallbacks) { try { tuple.Item1.Post(tuple.Item2, this); } catch (Exception ex) { LogWarning("Receive callback exception:" + ex); } } } }
/// <summary> /// Reads a string without advancing the read pointer /// </summary> public string PeekString() { int byteLen = (int)ReadVariableUInt32(); if (byteLen == 0) { return(String.Empty); } NetException.Assert(m_bitLength - m_readPosition >= (byteLen * 8), c_readOverflowError); if ((m_readPosition & 7) == 0) { // read directly string retval = System.Text.Encoding.UTF8.GetString(m_data, m_readPosition >> 3, byteLen); return(retval); } byte[] bytes = PeekBytes(byteLen); return(System.Text.Encoding.UTF8.GetString(bytes, 0, bytes.Length)); }
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly // seqNr is the actual nr received internal override void ReceiveAcknowledge(double now, int seqNr) { if (m_doFlowControl == false) { // we have no use for acks on this channel since we don't respect the window anyway m_connection.m_peer.LogVerbose("SuppressUnreliableUnorderedAcks sender/receiver mismatch!"); return; } // late (dupe), on time or early ack? int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart); if (relate < 0) { //m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr); return; // late/duplicate ack } if (relate == 0) { //m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr); // ack arrived right on time NetException.Assert(seqNr == m_windowStart); m_receivedAcks[m_windowStart] = false; m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; return; } // Advance window to this position m_receivedAcks[seqNr] = true; while (m_windowStart != seqNr) { m_receivedAcks[m_windowStart] = false; m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; } }
internal void ReceivedPong(float now, int pongNumber) { if (pongNumber != m_sentPingNumber) { m_peer.LogVerbose("Ping/Pong mismatch; dropped message?"); return; } m_timeoutDeadline = now + m_peerConfiguration.m_connectionTimeout; float rtt = now - m_sentPingTime; NetException.Assert(rtt >= 0); if (m_averageRoundtripTime < 0) { m_averageRoundtripTime = rtt; // initial estimate m_peer.LogDebug("Initiated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime)); } else { m_averageRoundtripTime = (m_averageRoundtripTime * 0.7f) + (float)(rtt * 0.3f); m_peer.LogVerbose("Updated average roundtrip time to " + NetTime.ToReadable(m_averageRoundtripTime)); } // update resend delay for all channels float resendDelay = GetResendDelay(); foreach (var chan in m_sendChannels) { var rchan = chan as NetReliableSenderChannel; if (rchan != null) { rchan.m_resendDelay = resendDelay; } } m_peer.LogVerbose("Timeout deadline pushed to " + m_timeoutDeadline); }
internal void Recycle(NetOutgoingMessage msg) { if (m_outgoingMessagesPool == null) { return; } NetException.Assert(m_outgoingMessagesPool.Contains(msg) == false, "Recyling already recycled message! Thread race?"); byte[] storage = msg.m_data; msg.m_data = null; // message fragments cannot be recycled // TODO: find a way to recycle large message after all fragments has been acknowledged; or? possibly better just to garbage collect them if (msg.m_fragmentGroup == 0) { Recycle(storage); } msg.Reset(); m_outgoingMessagesPool.Enqueue(msg); }
/// <summary> /// Send a message to a specific connection /// </summary> /// <param name="msg">The message to send</param> /// <param name="recipient">The recipient connection</param> /// <param name="method">How to deliver the message</param> /// <param name="sequenceChannel">Sequence channel within the delivery method</param> public NetSendResult SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod method, int sequenceChannel) { if (msg == null) { throw new ArgumentNullException("msg"); } if (recipient == null) { throw new ArgumentNullException("recipient"); } NetException.Assert( ((method != NetDeliveryMethod.Unreliable && method != NetDeliveryMethod.ReliableUnordered) || ((method == NetDeliveryMethod.Unreliable || method == NetDeliveryMethod.ReliableUnordered) && sequenceChannel == 0)), "Delivery method " + method + " cannot use sequence channels other than 0!" ); NetException.Assert(method != NetDeliveryMethod.Unknown, "Bad delivery method!"); if (msg.m_isSent) { throw new NetException("This message has already been sent! Use NetPeer.SendMessage() to send to multiple recipients efficiently"); } int len = msg.LengthBytes; if (len <= m_configuration.MaximumTransmissionUnit) { Interlocked.Increment(ref msg.m_recyclingCount); return(recipient.EnqueueMessage(msg, method, sequenceChannel)); } else { // message must be fragmented! SendFragmentedMessage(msg, new NetConnection[] { recipient }, method, sequenceChannel); return(NetSendResult.Queued); // could be different for each connection; Queued is "most true" } }
/// <summary> /// Устанавливает или сбрасывает большую книгу / по указанному индексу... /// </summary> public void Set(int bitIndex, bool value) { NetException.Assert(bitIndex >= 0 && bitIndex < m_capacity); int idx = bitIndex / 32; if (value) { if ((m_data[idx] & (1 << (bitIndex % 32))) == 0) { m_numBitsSet++; } m_data[idx] |= (1 << (bitIndex % 32)); } else { if ((m_data[idx] & (1 << (bitIndex % 32))) != 0) { m_numBitsSet--; } m_data[idx] &= (~(1 << (bitIndex % 32))); } }
/// <summary> /// Writes a signed integer using 1 to 32 bits /// </summary> public void Write(Int32 source, int numberOfBits) { NetException.Assert((numberOfBits > 0 && numberOfBits <= 32), "Write(int, numberOfBits) can only write between 1 and 32 bits"); EnsureBufferSize(m_bitLength + numberOfBits); if (numberOfBits != 32) { // make first bit sign int signBit = 1 << (numberOfBits - 1); if (source < 0) { source = (-source - 1) | signBit; } else { source &= (~signBit); } } NetBitWriter.WriteUInt32((uint)source, numberOfBits, m_data, m_bitLength); m_bitLength += numberOfBits; }
private void ExecuteSend(double now, NetOutgoingMessage message) { int seqNr = m_sendStart; m_sendStart = (m_sendStart + 1) % NetConstants.NumSequenceNumbers; // must increment recycle count here, since it's decremented in QueueSendMessage and we want to keep it for the future in case or resends // we will decrement once more in DestoreMessage for final recycling Interlocked.Increment(ref message.m_recyclingCount); m_connection.QueueSendMessage(message, seqNr); int storeIndex = seqNr % m_windowSize; NetException.Assert(m_storedMessages[storeIndex].Message == null); m_storedMessages[storeIndex].NumSent++; m_storedMessages[storeIndex].Message = message; m_storedMessages[storeIndex].LastSent = now; m_storedMessages[storeIndex].SequenceNumber = seqNr; return; }
/// <summary> /// Write a byte consisting of 1-8 bits to a buffer; assumes buffer is previously allocated /// </summary> public static void WriteByte(byte source, int numberOfBits, byte[] destination, int destBitOffset) { NetException.Assert(((numberOfBits >= 1) && (numberOfBits <= 8)), "Must write between 1 and 8 bits!"); // mask out unwanted bits in the source byte isrc = (byte)((uint)source & ((~(uint)0) >> (8 - numberOfBits))); int bytePtr = destBitOffset >> 3; int localBitLen = (destBitOffset % 8); if (localBitLen == 0) { destination[bytePtr] = (byte)isrc; return; } //destination[bytePtr] &= (byte)(255 >> (8 - localBitLen)); // clear before writing //destination[bytePtr] |= (byte)(isrc << localBitLen); // write first half destination[bytePtr] = (byte)( (uint)(destination[bytePtr] & (255 >> (8 - localBitLen))) | (uint)(isrc << localBitLen) ); // need write into next byte? if (localBitLen + numberOfBits > 8) { //destination[bytePtr + 1] &= (byte)(255 << localBitLen); // clear before writing //destination[bytePtr + 1] |= (byte)(isrc >> (8 - localBitLen)); // write second half destination[bytePtr + 1] = (byte)( (uint)(destination[bytePtr + 1] & (255 << localBitLen)) | (uint)(isrc >> (8 - localBitLen)) ); } return; }
/// <summary> /// Reads a signed integer stored in 1 to 64 bits, written using Write(Int64, Int32) /// </summary> public Int64 ReadInt64(int numberOfBits) { NetException.Assert(((numberOfBits > 0) && (numberOfBits <= 64)), "ReadInt64(bits) can only read between 1 and 64 bits"); return((long)ReadUInt64(numberOfBits)); }
internal override void ReceiveMessage(NetIncomingMessage message) { int relate = NetUtility.RelativeSequenceNumber(message.m_sequenceNumber, m_windowStart); // ack no matter what m_connection.QueueAck(message.m_receivedMessageType, message.m_sequenceNumber); if (relate == 0) { // Log("Received message #" + message.SequenceNumber + " right on time"); // // excellent, right on time // //m_peer.LogVerbose("Received RIGHT-ON-TIME " + message); AdvanceWindow(); m_peer.ReleaseMessage(message); // release withheld messages int nextSeqNr = (message.m_sequenceNumber + 1) % NetConstants.NumSequenceNumbers; while (m_earlyReceived[nextSeqNr % m_windowSize]) { message = m_withheldMessages[nextSeqNr % m_windowSize]; NetException.Assert(message != null); // remove it from withheld messages m_withheldMessages[nextSeqNr % m_windowSize] = null; m_peer.LogVerbose("Releasing withheld message #" + message); m_peer.ReleaseMessage(message); AdvanceWindow(); nextSeqNr++; } return; } if (relate < 0) { // duplicate m_connection.m_statistics.MessageDropped(); m_peer.LogVerbose("Received message #" + message.m_sequenceNumber + " DROPPING DUPLICATE"); return; } // relate > 0 = early message if (relate > m_windowSize) { // too early message! m_connection.m_statistics.MessageDropped(); m_peer.LogDebug("Received " + message + " TOO EARLY! Expected " + m_windowStart); return; } m_earlyReceived.Set(message.m_sequenceNumber % m_windowSize, true); m_peer.LogVerbose("Received " + message + " WITHHOLDING, waiting for " + m_windowStart); m_withheldMessages[message.m_sequenceNumber % m_windowSize] = message; }
private void SendCallBack(IAsyncResult res) { NetException.Assert(res.IsCompleted == true); m_socket.EndSendTo(res); }
private void Heartbeat() { VerifyNetworkThread(); double dnow = NetTime.Now; float now = (float)dnow; double delta = dnow - m_lastHeartbeat; int maxCHBpS = 1250 - m_connections.Count; if (maxCHBpS < 250) { maxCHBpS = 250; } if (delta > (1.0 / (double)maxCHBpS)) // max connection heartbeats/second max { m_frameCounter++; m_lastHeartbeat = dnow; // do handshake heartbeats if ((m_frameCounter % 3) == 0) { foreach (var kvp in m_handshakes) { NetConnection conn = kvp.Value as NetConnection; #if DEBUG // sanity check if (kvp.Key != kvp.Key) { LogWarning("Sanity fail! Connection in handshake list under wrong key!"); } #endif conn.UnconnectedHeartbeat(now); if (conn.m_status == NetConnectionStatus.Connected || conn.m_status == NetConnectionStatus.Disconnected) { #if DEBUG // sanity check if (conn.m_status == NetConnectionStatus.Disconnected && m_handshakes.ContainsKey(conn.RemoteEndpoint)) { LogWarning("Sanity fail! Handshakes list contained disconnected connection!"); m_handshakes.Remove(conn.RemoteEndpoint); } #endif break; // collection has been modified } } } #if DEBUG SendDelayedPackets(); #endif // update m_executeFlushSendQueue if (m_configuration.m_autoFlushSendQueue) { m_executeFlushSendQueue = true; } // do connection heartbeats lock (m_connections) { foreach (NetConnection conn in m_connections) { conn.Heartbeat(now, m_frameCounter); if (conn.m_status == NetConnectionStatus.Disconnected) { // // remove connection // m_connections.Remove(conn); m_connectionLookup.Remove(conn.RemoteEndpoint); break; // can't continue iteration here } } } m_executeFlushSendQueue = false; // send unsent unconnected messages NetTuple <IPEndPoint, NetOutgoingMessage> unsent; while (m_unsentUnconnectedMessages.TryDequeue(out unsent)) { NetOutgoingMessage om = unsent.Item2; bool connReset; int len = om.Encode(m_sendBuffer, 0, 0); SendPacket(len, unsent.Item1, 1, out connReset); Interlocked.Decrement(ref om.m_recyclingCount); if (om.m_recyclingCount <= 0) { Recycle(om); } } } // // read from socket // if (m_socket == null) { return; } if (!m_socket.Poll(1000, SelectMode.SelectRead)) // wait up to 1 ms for data to arrive { return; } //if (m_socket == null || m_socket.Available < 1) // return; do { int bytesReceived = 0; try { bytesReceived = m_socket.ReceiveFrom(m_receiveBuffer, 0, m_receiveBuffer.Length, SocketFlags.None, ref m_senderRemote); } catch (SocketException sx) { if (sx.SocketErrorCode == SocketError.ConnectionReset) { // connection reset by peer, aka connection forcibly closed aka "ICMP port unreachable" // we should shut down the connection; but m_senderRemote seemingly cannot be trusted, so which connection should we shut down?! // So, what to do? LogWarning("ConnectionReset"); return; } LogWarning(sx.ToString()); return; } if (bytesReceived < NetConstants.HeaderByteSize) { return; } //LogVerbose("Received " + bytesReceived + " bytes"); IPEndPoint ipsender = (IPEndPoint)m_senderRemote; if (ipsender.Port == 1900) { // UPnP response try { string resp = System.Text.Encoding.ASCII.GetString(m_receiveBuffer, 0, bytesReceived); if (resp.Contains("upnp:rootdevice")) { resp = resp.Substring(resp.ToLower().IndexOf("location:") + 9); resp = resp.Substring(0, resp.IndexOf("\r")).Trim(); m_upnp.ExtractServiceUrl(resp); return; } } catch { } } NetConnection sender = null; m_connectionLookup.TryGetValue(ipsender, out sender); double receiveTime = NetTime.Now; // // parse packet into messages // int numMessages = 0; int ptr = 0; while ((bytesReceived - ptr) >= NetConstants.HeaderByteSize) { // decode header // 8 bits - NetMessageType // 1 bit - Fragment? // 15 bits - Sequence number // 16 bits - Payload length in bits numMessages++; NetMessageType tp = (NetMessageType)m_receiveBuffer[ptr++]; byte low = m_receiveBuffer[ptr++]; byte high = m_receiveBuffer[ptr++]; bool isFragment = ((low & 1) == 1); ushort sequenceNumber = (ushort)((low >> 1) | (((int)high) << 7)); ushort payloadBitLength = (ushort)(m_receiveBuffer[ptr++] | (m_receiveBuffer[ptr++] << 8)); int payloadByteLength = NetUtility.BytesToHoldBits(payloadBitLength); if (bytesReceived - ptr < payloadByteLength) { LogWarning("Malformed packet; stated payload length " + payloadByteLength + ", remaining bytes " + (bytesReceived - ptr)); return; } try { NetException.Assert(tp <NetMessageType.Unused1 || tp> NetMessageType.Unused29); if (tp >= NetMessageType.LibraryError) { if (sender != null) { sender.ReceivedLibraryMessage(tp, ptr, payloadByteLength); } else { ReceivedUnconnectedLibraryMessage(receiveTime, ipsender, tp, ptr, payloadByteLength); } } else { if (sender == null && !m_configuration.IsMessageTypeEnabled(NetIncomingMessageType.UnconnectedData)) { return; // dropping unconnected message since it's not enabled } NetIncomingMessage msg = CreateIncomingMessage(NetIncomingMessageType.Data, payloadByteLength); msg.m_isFragment = isFragment; msg.m_receiveTime = receiveTime; msg.m_sequenceNumber = sequenceNumber; msg.m_receivedMessageType = tp; msg.m_senderConnection = sender; msg.m_senderEndpoint = ipsender; msg.m_bitLength = payloadBitLength; Buffer.BlockCopy(m_receiveBuffer, ptr, msg.m_data, 0, payloadByteLength); if (sender != null) { if (tp == NetMessageType.Unconnected) { // We're connected; but we can still send unconnected messages to this peer msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData; ReleaseMessage(msg); } else { // connected application (non-library) message sender.ReceivedMessage(msg); } } else { // at this point we know the message type is enabled // unconnected application (non-library) message msg.m_incomingMessageType = NetIncomingMessageType.UnconnectedData; ReleaseMessage(msg); } } } catch (Exception ex) { LogError("Packet parsing error: " + ex.Message + " from " + ipsender); } ptr += payloadByteLength; } m_statistics.PacketReceived(bytesReceived, numMessages); if (sender != null) { sender.m_statistics.PacketReceived(bytesReceived, numMessages); } } while (m_socket.Available > 0); }
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly // seqNr is the actual nr received internal override void ReceiveAcknowledge(float now, int seqNr) { // late (dupe), on time or early ack? int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart); if (relate < 0) { //m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr); return; // late/duplicate ack } if (relate == 0) { //m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr); // ack arrived right on time NetException.Assert(seqNr == m_windowStart); m_receivedAcks[m_windowStart] = false; DestoreMessage(m_windowStart % m_windowSize); m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; // advance window if we already have early acks while (m_receivedAcks.Get(m_windowStart)) { //m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "..."); m_receivedAcks[m_windowStart] = false; DestoreMessage(m_windowStart % m_windowSize); NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; //m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart); } return; } // // early ack... (if it has been sent!) // // If it has been sent either the m_windowStart message was lost // ... or the ack for that message was lost // //m_connection.m_peer.LogDebug("Received early ack for #" + seqNr); int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart); if (sendRelate <= 0) { // yes, we've sent this message - it's an early (but valid) ack if (m_receivedAcks[seqNr]) { // we've already destored/been acked for this message } else { m_receivedAcks[seqNr] = true; } } else if (sendRelate > 0) { // This is a bug in Lidgren (that I don't have time to mess around with) (-AR) // Disconnecting causes the channel to Reset() (resets pending sequence number), but somehow we still receive acks. // (Status gets set to Disconnected after the Reset, but seems to all be on the network thread, so not a race condition) if (m_connection == null || m_connection.m_status == NetConnectionStatus.Disconnected) { return; // Let's not call NetException.Assert! } // uh... we haven't sent this message yet? Weird, dupe or error... NetException.Assert(false, "Got ack for message not yet sent?"); return; } // Ok, lets resend all missing acks int rnr = seqNr; do { rnr--; if (rnr < 0) { rnr = NetConstants.NumSequenceNumbers - 1; } if (m_receivedAcks[rnr]) { // m_connection.m_peer.LogDebug("Not resending #" + rnr + " (since we got ack)"); } else { int slot = rnr % m_windowSize; NetException.Assert(m_storedMessages[slot].Message != null); if (m_storedMessages[slot].NumSent == 1) { // just sent once; resend immediately since we found gap in ack sequence NetOutgoingMessage rmsg = m_storedMessages[slot].Message; //m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")"); if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35f)) { // already resent recently } else { m_storedMessages[slot].LastSent = now; m_storedMessages[slot].NumSent++; m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence); m_connection.QueueSendMessage(rmsg, rnr); } } } } while (rnr != m_windowStart); }
// call this regularely internal override void SendQueuedMessages(float now) { // // resends // int len = m_storedMessages.Length; for (int i = 0; i < len; ++i) { NetStoredReliableMessage msg = m_storedMessages[i]; NetOutgoingMessage om = msg.Message; if (om == null) { continue; } float t = msg.LastSent; if (t > 0 && (now - t) > m_resendDelay) { // deduce sequence number /* * int startSlot = m_windowStart % m_windowSize; * int seqNr = m_windowStart; * while (startSlot != i) * { * startSlot--; * if (startSlot < 0) * startSlot = m_windowSize - 1; * seqNr--; * } */ //m_connection.m_peer.LogVerbose("Resending due to delay #" + msg.SequenceNumber + " " + om.ToString()); m_connection.m_statistics.MessageResent(MessageResendReason.Delay); m_connection.QueueSendMessage(om, msg.SequenceNumber); msg.LastSent = now; ++msg.NumSent; } } int num = GetAllowedSends(); if (num < 1) { return; } // queued sends while (m_queuedSends.Count > 0 && num > 0) { NetOutgoingMessage om; if (m_queuedSends.TryDequeue(out om)) { ExecuteSend(now, om); } num--; NetException.Assert(num == GetAllowedSends()); } }
// call this regularely internal override void SendQueuedMessages(double now) { // // resends // m_anyStoredResends = false; for (int i = 0; i < m_storedMessages.Length; i++) { var storedMsg = m_storedMessages[i]; NetOutgoingMessage om = storedMsg.Message; if (om == null) { continue; } m_anyStoredResends = true; double t = storedMsg.LastSent; if (t > 0 && (now - t) > m_resendDelay) { // deduce sequence number /* * int startSlot = m_windowStart % m_windowSize; * int seqNr = m_windowStart; * while (startSlot != i) * { * startSlot--; * if (startSlot < 0) * startSlot = m_windowSize - 1; * seqNr--; * } */ //m_connection.m_peer.LogVerbose("Resending due to delay #" + m_storedMessages[i].SequenceNumber + " " + om.ToString()); m_connection.m_statistics.MessageResent(MessageResendReason.Delay); Interlocked.Increment(ref om.m_recyclingCount); // increment this since it's being decremented in QueueSendMessage m_connection.QueueSendMessage(om, storedMsg.SequenceNumber); m_storedMessages[i].LastSent = now; m_storedMessages[i].NumSent++; } } int num = GetAllowedSends(); if (num < 1) { return; } // queued sends while (num > 0 && m_queuedSends.Count > 0) { NetOutgoingMessage om; if (m_queuedSends.TryDequeue(out om)) { ExecuteSend(now, om); } num--; NetException.Assert(num == GetAllowedSends()); } }
// remoteWindowStart is remote expected sequence number; everything below this has arrived properly // seqNr is the actual nr received internal override void ReceiveAcknowledge(double now, int seqNr) { // late (dupe), on time or early ack? int relate = NetUtility.RelativeSequenceNumber(seqNr, m_windowStart); if (relate < 0) { //m_connection.m_peer.LogDebug("Received late/dupe ack for #" + seqNr); return; // late/duplicate ack } if (relate == 0) { //m_connection.m_peer.LogDebug("Received right-on-time ack for #" + seqNr); // ack arrived right on time NetException.Assert(seqNr == m_windowStart); bool resetTimeout; m_receivedAcks[m_windowStart] = false; DestoreMessage(now, m_windowStart % m_windowSize, out resetTimeout); m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; // advance window if we already have early acks while (m_receivedAcks.Get(m_windowStart)) { //m_connection.m_peer.LogDebug("Using early ack for #" + m_windowStart + "..."); m_receivedAcks[m_windowStart] = false; bool rt; DestoreMessage(now, m_windowStart % m_windowSize, out rt); resetTimeout |= rt; NetException.Assert(m_storedMessages[m_windowStart % m_windowSize].Message == null); // should already be destored m_windowStart = (m_windowStart + 1) % NetConstants.NumSequenceNumbers; //m_connection.m_peer.LogDebug("Advancing window to #" + m_windowStart); } if (resetTimeout) { m_connection.ResetTimeout(now); } return; } // // early ack... (if it has been sent!) // // If it has been sent either the m_windowStart message was lost // ... or the ack for that message was lost // //m_connection.m_peer.LogDebug("Received early ack for #" + seqNr); int sendRelate = NetUtility.RelativeSequenceNumber(seqNr, m_sendStart); if (sendRelate <= 0) { // yes, we've sent this message - it's an early (but valid) ack if (m_receivedAcks[seqNr]) { // we've already destored/been acked for this message } else { m_receivedAcks[seqNr] = true; } } else if (sendRelate > 0) { // uh... we haven't sent this message yet? Weird, dupe or error... NetException.Assert(false, "Got ack for message not yet sent?"); return; } // Ok, lets resend all missing acks int rnr = seqNr; do { rnr--; if (rnr < 0) { rnr = NetConstants.NumSequenceNumbers - 1; } if (m_receivedAcks[rnr]) { // m_connection.m_peer.LogDebug("Not resending #" + rnr + " (since we got ack)"); } else { int slot = rnr % m_windowSize; NetException.Assert(m_storedMessages[slot].Message != null); if (m_storedMessages[slot].NumSent == 1) { // just sent once; resend immediately since we found gap in ack sequence NetOutgoingMessage rmsg = m_storedMessages[slot].Message; //m_connection.m_peer.LogVerbose("Resending #" + rnr + " (" + rmsg + ")"); if (now - m_storedMessages[slot].LastSent < (m_resendDelay * 0.35)) { // already resent recently } else { m_storedMessages[slot].LastSent = now; m_storedMessages[slot].NumSent++; m_connection.m_statistics.MessageResent(MessageResendReason.HoleInSequence); Interlocked.Increment(ref rmsg.m_recyclingCount); // increment this since it's being decremented in QueueSendMessage m_connection.QueueSendMessage(rmsg, rnr); } } } } while (rnr != m_windowStart); }