internal void StoreMessage(double now, OutgoingNetMessage msg) { int chanBufIdx = (int)msg.m_sequenceChannel - (int)NetChannel.ReliableUnordered; List <OutgoingNetMessage> list = m_storedMessages[chanBufIdx]; if (list == null) { list = new List <OutgoingNetMessage>(); m_storedMessages[chanBufIdx] = list; } list.Add(msg); // schedule resend double nextResend = now + m_owner.Configuration.ResendFunction(msg.m_numSent, m_currentAvgRoundtrip); msg.m_nextResend = nextResend; // if (msg.m_numSent >= 10) UnityEngine.Debug.Log(now + ": Try no " + msg.m_numSent + ": " + NetUtility.BytesToHex(msg.m_data.ToArray())); m_owner.LogVerbose("Stored " + msg + " @ " + NetTime.ToMillis(now) + " next resend in " + NetTime.ToMillis(msg.m_nextResend - now) + " ms", this); // earliest? if (nextResend < m_earliestResend[chanBufIdx]) { m_earliestResend[chanBufIdx] = nextResend; } }
internal void StoreMessage(double now, OutgoingNetMessage msg) { int chanBufIdx = (int)msg.m_sequenceChannel - (int)NetChannel.ReliableUnordered; List <OutgoingNetMessage> list = m_storedMessages[chanBufIdx]; if (list == null) { list = new List <OutgoingNetMessage>(); m_storedMessages[chanBufIdx] = list; } list.Add(msg); // schedule resend float multiplier = (1 + (msg.m_numSent * msg.m_numSent)) * m_owner.m_config.m_resendTimeMultiplier; double nextResend = now + (0.025f + (float)m_currentAvgRoundtrip * 1.1f * multiplier); msg.m_nextResend = nextResend; m_owner.LogVerbose("Stored " + msg + " @ " + NetTime.ToMillis(now) + " next resend in " + NetTime.ToMillis(msg.m_nextResend - now) + " ms", this); // earliest? if (nextResend < m_earliestResend[chanBufIdx]) { m_earliestResend[chanBufIdx] = nextResend; } }
internal void AddConnection(double now, NetConnection conn) { conn.SetStatus(NetConnectionStatus.Connecting, "Connecting"); lock (m_connections) { if (m_connections.Contains(conn)) { // already added conn.m_approved = true; // just to be sure return; } } LogWrite("Adding connection " + conn); // send response; even if connected OutgoingNetMessage response = CreateSystemMessage(NetSystemType.ConnectResponse); if (conn.LocalHailData != null) { response.m_data.Write(conn.LocalHailData); } conn.m_unsentMessages.Enqueue(response); conn.m_handshakeInitiated = now; conn.m_approved = true; lock (m_connections) m_connections.Add(conn); m_connectionLookup[conn.m_remoteEndPoint] = conn; }
internal OutgoingNetMessage CreateSystemMessage(NetSystemType systemType) { OutgoingNetMessage msg = CreateOutgoingMessage(); msg.m_type = NetMessageLibraryType.System; msg.m_sequenceChannel = NetChannel.Unreliable; msg.m_sequenceNumber = 0; msg.m_data.Write((byte)systemType); return(msg); }
/// <summary> /// Creates an outgoing net message /// </summary> internal OutgoingNetMessage CreateOutgoingMessage() { // no recycling for messages OutgoingNetMessage msg = new OutgoingNetMessage(); msg.m_sequenceNumber = -1; msg.m_numSent = 0; msg.m_nextResend = double.MaxValue; msg.m_msgType = NetMessageType.Data; msg.m_data = CreateBuffer(); return(msg); }
private void AssignSequenceNumber(OutgoingNetMessage msg) { int idx = (int)msg.m_sequenceChannel; int nr = m_nextSequenceToSend[idx]; msg.m_sequenceNumber = nr; nr++; if (nr >= NetConstants.NumSequenceNumbers) { nr = 0; } m_nextSequenceToSend[idx] = nr; }
internal void Connect() { m_isInitiator = true; m_handshakeInitiated = NetTime.Now; m_futureClose = double.MaxValue; m_futureDisconnectReason = null; m_owner.LogVerbose("Sending Connect request to " + m_remoteEndPoint, this); OutgoingNetMessage msg = m_owner.CreateSystemMessage(NetSystemType.Connect); msg.m_data.Write(m_owner.Configuration.ApplicationIdentifier); msg.m_data.Write(m_owner.m_randomIdentifier); if (m_localHailData != null && m_localHailData.Length > 0) { msg.m_data.Write(m_localHailData); } m_unsentMessages.Enqueue(msg); SetStatus(NetConnectionStatus.Connecting, "Connecting"); }
/// <summary> /// Create ack message(s) for sending /// </summary> private void CreateAckMessages() { int mtuBits = ((m_owner.m_config.m_maximumTransmissionUnit - 12) / 3) * 8; OutgoingNetMessage ackMsg = null; int numAcks = m_acknowledgesToSend.Count; for (int i = 0; i < numAcks; i++) { if (ackMsg == null) { ackMsg = m_owner.CreateOutgoingMessage(); ackMsg.m_sequenceChannel = NetChannel.Unreliable; ackMsg.m_type = NetMessageLibraryType.Acknowledge; } int ack = m_acknowledgesToSend.Dequeue(); ackMsg.m_data.Write((byte)((ack >> 16) & 255)); ackMsg.m_data.Write((byte)(ack & 255)); ackMsg.m_data.Write((byte)((ack >> 8) & 255)); //NetChannel ac = (NetChannel)(ack >> 16); //int asn = ack & ushort.MaxValue; //LogVerbose("Sending ack " + ac + "|" + asn); if (ackMsg.m_data.LengthBits >= mtuBits && m_acknowledgesToSend.Count > 0) { // send and begin again m_unsentMessages.Enqueue(ackMsg); ackMsg = null; } } if (ackMsg != null) { m_unsentMessages.EnqueueFirst(ackMsg); // push acks to front of queue } m_statistics.CountAcknowledgesSent(numAcks); }
/* * internal void HandleUserMessage(NetMessage msg) * { * int seqNr = msg.m_sequenceNumber; * int chanNr = (int)msg.m_sequenceChannel; * bool isDuplicate = false; * * int relation = RelateToExpected(seqNr, chanNr, out isDuplicate); * * // * // Unreliable * // * if (msg.m_sequenceChannel == NetChannel.Unreliable) * { * // It's all good; add message * if (isDuplicate) * { * m_statistics.CountDuplicateMessage(msg); * m_owner.LogVerbose("Rejecting duplicate " + msg, this); * } * else * { * AcceptMessage(msg); * } * return; * } * * // * // Reliable unordered * // * if (msg.m_sequenceChannel == NetChannel.ReliableUnordered) * { * // send acknowledge (even if duplicate) * m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber); * * if (isDuplicate) * { * m_statistics.CountDuplicateMessage(msg); * m_owner.LogVerbose("Rejecting duplicate " + msg, this); * return; // reject duplicates * } * * // It's good; add message * AcceptMessage(msg); * * return; * } * * ushort nextSeq = (ushort)(seqNr + 1); * * if (chanNr < (int)NetChannel.ReliableInOrder1) * { * // * // Sequenced * // * if (relation < 0) * { * // late sequenced message * m_statistics.CountDroppedSequencedMessage(); * m_owner.LogVerbose("Dropping late sequenced " + msg, this); * return; * } * * // It's good; add message * AcceptMessage(msg); * * m_nextExpectedSequence[chanNr] = nextSeq; * return; * } * else * { * // * // Ordered * // * * // send ack (regardless) * m_acknowledgesToSend.Enqueue((chanNr << 16) | msg.m_sequenceNumber); * * if (relation < 0) * { * // late ordered message #if DEBUG * if (!isDuplicate) * m_owner.LogWrite("Ouch, weird! Late ordered message that's NOT a duplicate?! seqNr: " + seqNr + " expecting: " + m_nextExpectedSequence[chanNr], this); #endif * // must be duplicate * m_owner.LogVerbose("Dropping duplicate message " + seqNr, this); * m_statistics.CountDuplicateMessage(msg); * return; // rejected; don't advance next expected * } * * if (relation > 0) * { * // early message; withhold ordered * m_owner.LogVerbose("Withholding " + msg + " (expecting " + m_nextExpectedSequence[chanNr] + ")", this); * m_withheldMessages.Add(msg); * return; // return without advancing next expected * } * * // It's right on time! * AcceptMessage(msg); * * // ordered; release other withheld messages? * bool released = false; * do * { * released = false; * foreach (NetMessage wm in m_withheldMessages) * { * if ((int)wm.m_sequenceChannel == chanNr && wm.m_sequenceNumber == nextSeq) * { * m_owner.LogVerbose("Releasing withheld message " + wm, this); * m_withheldMessages.Remove(wm); * AcceptMessage(wm); * // no need to set rounds for this message; it was one when first related() and withheld * nextSeq++; * if (nextSeq >= NetConstants.NumSequenceNumbers) * nextSeq -= NetConstants.NumSequenceNumbers; * released = true; * break; * } * } * } while (released); * } * * // Common to Sequenced and Ordered * * //m_owner.LogVerbose("Setting next expected for " + (NetChannel)chanNr + " to " + nextSeq); * m_nextExpectedSequence[chanNr] = nextSeq; * * return; * } */ internal void HandleSystemMessage(IncomingNetMessage msg, double now) { msg.m_data.Position = 0; NetSystemType sysType = (NetSystemType)msg.m_data.ReadByte(); OutgoingNetMessage response = null; switch (sysType) { case NetSystemType.Disconnect: if (m_status == NetConnectionStatus.Disconnected) { return; } Disconnect(msg.m_data.ReadString(), 0.75f + ((float)m_currentAvgRoundtrip * 4), false, false); break; case NetSystemType.ConnectionRejected: string reason = msg.m_data.ReadString(); m_owner.NotifyApplication(NetMessageType.ConnectionRejected, reason, msg.m_sender, msg.m_senderEndPoint); Disconnect(reason, 0.0f, false, true); break; case NetSystemType.Connect: // ConnectReponse must have been losts string appIdent = msg.m_data.ReadString(); if (appIdent != m_owner.m_config.ApplicationIdentifier) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Connect for different application identification received: " + appIdent, null, msg.m_senderEndPoint); } return; } // read random identifer byte[] rnd = msg.m_data.ReadBytes(8); if (NetUtility.CompareElements(rnd, m_owner.m_randomIdentifier)) { // don't allow self-connect if ((m_owner.m_enabledMessageTypes & NetMessageType.ConnectionRejected) == NetMessageType.ConnectionRejected) { m_owner.NotifyApplication(NetMessageType.ConnectionRejected, "Connection to self not allowed", null, msg.m_senderEndPoint); } return; } // read hail data m_remoteHailData = null; int hailBytesCount = (msg.m_data.LengthBits - msg.m_data.Position) / 8; if (hailBytesCount > 0) { m_remoteHailData = msg.m_data.ReadBytes(hailBytesCount); } // finalize disconnect if it's in process if (m_status == NetConnectionStatus.Disconnecting) { FinalizeDisconnect(); } // send response; even if connected response = m_owner.CreateSystemMessage(NetSystemType.ConnectResponse); if (m_localHailData != null) { response.m_data.Write(m_localHailData); } m_unsentMessages.Enqueue(response); break; case NetSystemType.ConnectResponse: if (m_status != NetConnectionStatus.Connecting && m_status != NetConnectionStatus.Connected) { m_owner.LogWrite("Received connection response but we're not connecting...", this); return; } // read hail data m_remoteHailData = null; int numHailBytes = (msg.m_data.LengthBits - msg.m_data.Position) / 8; if (numHailBytes > 0) { m_remoteHailData = msg.m_data.ReadBytes(numHailBytes); } // Send connectionestablished response = m_owner.CreateSystemMessage(NetSystemType.ConnectionEstablished); if (m_localHailData != null) { response.m_data.Write(m_localHailData); } m_unsentMessages.Enqueue(response); // send first ping 250ms after connected m_lastSentPing = now - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f); m_statistics.Reset(); SetInitialAveragePing(now - m_handshakeInitiated); SetStatus(NetConnectionStatus.Connected, "Connected"); break; case NetSystemType.ConnectionEstablished: if (m_status != NetConnectionStatus.Connecting) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Received connection response but we're not connecting...", this, msg.m_senderEndPoint); } return; } // read hail data if (m_remoteHailData == null) { int hbc = (msg.m_data.LengthBits - msg.m_data.Position) / 8; if (hbc > 0) { m_remoteHailData = msg.m_data.ReadBytes(hbc); } } // send first ping 100-350ms after connected m_lastSentPing = now - m_owner.Configuration.PingFrequency + 0.1 + (NetRandom.Instance.NextFloat() * 0.25f); m_statistics.Reset(); SetInitialAveragePing(now - m_handshakeInitiated); SetStatus(NetConnectionStatus.Connected, "Connected"); break; case NetSystemType.Ping: // also accepted as ConnectionEstablished if (m_isInitiator == false && m_status == NetConnectionStatus.Connecting) { m_owner.LogWrite("Received ping; interpreted as ConnectionEstablished", this); m_statistics.Reset(); SetInitialAveragePing(now - m_handshakeInitiated); SetStatus(NetConnectionStatus.Connected, "Connected"); } //LogWrite("Received ping; sending pong..."); SendPong(m_owner, m_remoteEndPoint, now); break; case NetSystemType.Pong: double twoWayLatency = now - m_lastSentPing; if (twoWayLatency < 0) { break; } ReceivedPong(twoWayLatency, msg); break; case NetSystemType.StringTableAck: ushort val = msg.m_data.ReadUInt16(); StringTableAcknowledgeReceived(val); break; default: m_owner.LogWrite("Undefined behaviour in NetConnection for system message " + sysType, this); break; } }
internal void SendUnsentMessages(double now) { // Add any acknowledges to unsent messages if (m_acknowledgesToSend.Count > 0) { if (m_unsentMessages.Count < 1) { // Wait before sending acknowledges? if (m_ackMaxDelayTime > 0.0f) { if (m_ackWithholdingStarted == 0.0) { m_ackWithholdingStarted = now; } else { if (now - m_ackWithholdingStarted < m_ackMaxDelayTime) { return; // don't send (only) acks just yet } // send acks "explicitly" ie. without any other message being sent m_ackWithholdingStarted = 0.0; } } } // create ack messages and add to m_unsentMessages CreateAckMessages(); } if (m_unsentMessages.Count < 1) { return; } // throttling float throttle = m_owner.m_config.ThrottleBytesPerSecond; float maxSendBytes = float.MaxValue; if (throttle > 0) { double frameLength = now - m_lastSentUnsentMessages; //int wasDebt = (int)m_throttleDebt; if (m_throttleDebt > 0) { m_throttleDebt -= (float)(frameLength * (double)m_owner.m_config.ThrottleBytesPerSecond); } //int nowDebt = (int)m_throttleDebt; //if (nowDebt != wasDebt) // LogWrite("THROTTLE worked off -" + (nowDebt - wasDebt) + " bytes = " + m_throttleDebt); m_lastSentUnsentMessages = now; maxSendBytes = throttle - m_throttleDebt; if (maxSendBytes < 0) { return; // throttling; no bytes allowed to be sent } } int mtu = m_owner.Configuration.MaximumTransmissionUnit; bool useCoalescing = m_owner.Configuration.UseMessageCoalescing; int messagesInPacket = 0; NetBuffer sendBuffer = m_owner.m_sendBuffer; sendBuffer.Reset(); while (m_unsentMessages.Count > 0) { OutgoingNetMessage msg = m_unsentMessages.Peek(); int estimatedMessageSize = msg.m_data.LengthBytes + 5; // check if this message fits the throttle window if (estimatedMessageSize > maxSendBytes) // TODO: Allow at last one message if no debt { break; } // need to send packet and start a new one? if (messagesInPacket > 0) { if (!useCoalescing || (sendBuffer.LengthBytes + estimatedMessageSize > mtu)) { m_owner.SendPacket(m_remoteEndPoint); int sendLen = sendBuffer.LengthBytes; m_statistics.CountPacketSent(sendLen); //LogWrite("THROTTLE Send packet +" + sendLen + " bytes = " + m_throttleDebt + " (maxSendBytes " + maxSendBytes + " estimated " + estimatedMessageSize + ")"); m_throttleDebt += sendLen; sendBuffer.Reset(); } } if (msg.m_sequenceNumber == -1) { AssignSequenceNumber(msg); } // pop and encode message m_unsentMessages.Dequeue(); int pre = sendBuffer.m_bitLength; msg.m_data.m_readPosition = 0; msg.Encode(sendBuffer); int encLen = (sendBuffer.m_bitLength - pre) / 8; m_statistics.CountMessageSent(msg, encLen); maxSendBytes -= encLen; if (msg.m_sequenceChannel >= NetChannel.ReliableUnordered) { // reliable; store message (incl. buffer) msg.m_numSent++; StoreMessage(now, msg); } else { // not reliable, don't store - recycle... NetBuffer b = msg.m_data; b.m_refCount--; msg.m_data = null; // ... unless someone else is using the buffer if (b.m_refCount <= 0) { m_owner.RecycleBuffer(b); } //m_owner.m_messagePool.Push(msg); } messagesInPacket++; } // send current packet if (messagesInPacket > 0) { m_owner.SendPacket(m_remoteEndPoint); int sendLen = sendBuffer.LengthBytes; m_statistics.CountPacketSent(sendLen); //LogWrite("THROTTLE Send packet +" + sendLen + " bytes = " + m_throttleDebt); m_throttleDebt += sendLen; } }
internal void Heartbeat(double now) { if (m_status == NetConnectionStatus.Disconnected) { return; } //CongestionHeartbeat(now); // drain messages from application into main unsent list lock (m_lockedUnsentMessages) { OutgoingNetMessage lm; while ((lm = m_lockedUnsentMessages.Dequeue()) != null) { m_unsentMessages.Enqueue(lm); } } if (m_status == NetConnectionStatus.Connecting) { if (now - m_handshakeInitiated > m_owner.Configuration.HandshakeAttemptRepeatDelay) { if (m_handshakeAttempts >= m_owner.Configuration.HandshakeAttemptsMaxCount) { Disconnect("No answer from remote host", 0, false, true); return; } m_handshakeAttempts++; if (m_isInitiator) { m_owner.LogWrite("Re-sending Connect", this); Connect(); } else { m_owner.LogWrite("Re-sending ConnectResponse", this); m_handshakeInitiated = now; OutgoingNetMessage response = m_owner.CreateSystemMessage(NetSystemType.ConnectResponse); if (m_localHailData != null) { response.m_data.Write(m_localHailData); } m_unsentMessages.Enqueue(response); } } } else if (m_status == NetConnectionStatus.Connected) { // send ping? CheckPing(now); } if (m_requestDisconnect) { InitiateDisconnect(); } if (now > m_futureClose) { FinalizeDisconnect(); } // Resend all packets that has reached a mature age ResendMessages(now); // send all unsent messages SendUnsentMessages(now); }
internal void HandleAckMessage(double now, IncomingNetMessage ackMessage) { int len = ackMessage.m_data.LengthBytes; if ((len % 3) != 0) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Malformed ack message; length must be multiple of 3; it's " + len, this, ackMessage.m_senderEndPoint); } return; } for (int i = 0; i < len; i += 3) //for each channel + seq nbr in ACK { NetChannel chan = (NetChannel)ackMessage.m_data.ReadByte(); int seqNr = ackMessage.m_data.ReadUInt16(); // LogWrite("Acknowledgement received: " + chan + "|" + seqNr); m_statistics.CountAcknowledgesReceived(1); // remove saved message int relChanNr = (int)chan - (int)NetChannel.ReliableUnordered; if (relChanNr < 0) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Malformed ack message; indicated netchannel " + chan, this, ackMessage.m_senderEndPoint); } continue; } List <OutgoingNetMessage> list = m_storedMessages[relChanNr]; if (list != null) { int cnt = list.Count; if (cnt > 0) { for (int j = 0; j < cnt; j++) //for each stored message on channel { OutgoingNetMessage msg = list[j]; if (msg.m_sequenceNumber == seqNr) //find correct message { // if (msg.m_numSent >= 10) UnityEngine.Debug.Log(now + ": Ack for " + msg.m_numSent + ": " + NetUtility.BytesToHex(msg.m_data.ToArray())); //LogWrite("Removed stored message: " + msg); list.RemoveAt(j); // reduce estimated amount of packets on wire //CongestionCountAck(msg.m_packetNumber); // fire receipt if (msg.m_receiptData != null) { m_owner.LogVerbose("Got ack, removed from storage: " + msg + " firing receipt; " + msg.m_receiptData, this); m_owner.FireReceipt(this, msg.m_receiptData); } else { m_owner.LogVerbose("Got ack, removed from storage: " + msg, this); } // recycle msg.m_data.m_refCount--; if (msg.m_data.m_refCount <= 0) { m_owner.RecycleBuffer(msg.m_data); // time to recycle buffer } msg.m_data = null; //m_owner.m_messagePool.Push(msg); #if !NO_NAK if (j > 0) { int k; for (k = 0; k < j; k++) //for each message stored prior to the one matching seq nbr { var m = list[k]; if (m.m_sequenceNumber > seqNr) { break; } // Re-enqueue message in unsent list m_owner.LogVerbose("Implicit NAK Resending " + m + " now: " + NetTime.NowInMillis + " nextResend: " + NetTime.ToMillis(m.m_nextResend), this); m_statistics.CountMessageResent(m.m_type); m_unsentMessages.Enqueue(m); } list.RemoveRange(0, k); } #endif break; //exit stored message loop since this was the message corresponding to seq nbr //now returning to next sequence number in ACK packet } } } } } // recycle NetBuffer rb = ackMessage.m_data; rb.m_refCount = 0; // ack messages can't be used by more than one message ackMessage.m_data = null; m_owner.RecycleBuffer(rb); //m_owner.m_messagePool.Push(ackMessage); }
/// <summary> /// Creates an outgoing net message /// </summary> internal OutgoingNetMessage CreateOutgoingMessage() { // no recycling for messages OutgoingNetMessage msg = new OutgoingNetMessage(); msg.m_sequenceNumber = -1; msg.m_numSent = 0; msg.m_nextResend = double.MaxValue; msg.m_msgType = NetMessageType.Data; msg.m_data = CreateBuffer(); return msg; }
internal void HandleAckMessage(IncomingNetMessage ackMessage) { int len = ackMessage.m_data.LengthBytes; if ((len % 3) != 0) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Malformed ack message; length must be multiple of 3; it's " + len, this, ackMessage.m_senderEndPoint); } return; } for (int i = 0; i < len; i += 3) { NetChannel chan = (NetChannel)ackMessage.m_data.ReadByte(); int seqNr = ackMessage.m_data.ReadUInt16(); // LogWrite("Acknowledgement received: " + chan + "|" + seqNr); m_statistics.CountAcknowledgesReceived(1); // remove saved message int chanIdx = (int)chan - (int)NetChannel.ReliableUnordered; if (chanIdx < 0) { if ((m_owner.m_enabledMessageTypes & NetMessageType.BadMessageReceived) == NetMessageType.BadMessageReceived) { m_owner.NotifyApplication(NetMessageType.BadMessageReceived, "Malformed ack message; indicated netchannel " + chan, this, ackMessage.m_senderEndPoint); } continue; } List <OutgoingNetMessage> list = m_storedMessages[chanIdx]; if (list != null) { int cnt = list.Count; if (cnt > 0) { for (int o = 0; o < cnt; o++) { OutgoingNetMessage msg = list[o]; if (msg.m_sequenceNumber == seqNr) { //LogWrite("Removed stored message: " + msg); list.RemoveAt(o); // reduce estimated amount of packets on wire //CongestionCountAck(msg.m_packetNumber); // fire receipt if (msg.m_receiptData != null) { m_owner.LogVerbose("Got ack, removed from storage: " + msg + " firing receipt; " + msg.m_receiptData, this); m_owner.FireReceipt(this, msg.m_receiptData); } else { m_owner.LogVerbose("Got ack, removed from storage: " + msg, this); } // recycle msg.m_data.m_refCount--; if (msg.m_data.m_refCount <= 0) { m_owner.RecycleBuffer(msg.m_data); // time to recycle buffer } msg.m_data = null; //m_owner.m_messagePool.Push(msg); break; } } } } } // recycle NetBuffer rb = ackMessage.m_data; rb.m_refCount = 0; // ack messages can't be used by more than one message ackMessage.m_data = null; m_owner.RecycleBuffer(rb); //m_owner.m_messagePool.Push(ackMessage); }
internal void StoreMessage(double now, OutgoingNetMessage msg) { int chanBufIdx = (int)msg.m_sequenceChannel - (int)NetChannel.ReliableUnordered; List<OutgoingNetMessage> list = m_storedMessages[chanBufIdx]; if (list == null) { list = new List<OutgoingNetMessage>(); m_storedMessages[chanBufIdx] = list; } list.Add(msg); // schedule resend float multiplier = (1 + (msg.m_numSent * msg.m_numSent)) * m_owner.m_config.m_resendTimeMultiplier; double nextResend = now + (0.025f + (float)m_currentAvgRoundtrip * 1.1f * multiplier); msg.m_nextResend = nextResend; m_owner.LogVerbose("Stored " + msg + " @ " + NetTime.ToMillis(now) + " next resend in " + NetTime.ToMillis(msg.m_nextResend - now) + " ms", this); // earliest? if (nextResend < m_earliestResend[chanBufIdx]) m_earliestResend[chanBufIdx] = nextResend; }
private void AssignSequenceNumber(OutgoingNetMessage msg) { int idx = (int)msg.m_sequenceChannel; int nr = m_nextSequenceToSend[idx]; msg.m_sequenceNumber = nr; nr++; if (nr >= NetConstants.NumSequenceNumbers) nr = 0; m_nextSequenceToSend[idx] = nr; }
// TODO: Use this with TRUE isLibraryThread for internal sendings (acks etc) internal void SendMessage(NetBuffer data, NetChannel channel, NetBuffer receiptData, bool isLibraryThread) { if (m_status != NetConnectionStatus.Connected) { throw new NetException("Status must be Connected to send messages"); } if (data.LengthBytes > m_owner.m_config.m_maximumTransmissionUnit) { // // Fragmented message // int dataLen = data.LengthBytes; int chunkSize = m_owner.m_config.m_maximumTransmissionUnit - 10; // header int numFragments = dataLen / chunkSize; if (chunkSize * numFragments < dataLen) { numFragments++; } ushort fragId = m_nextSendFragmentId++; for (int i = 0; i < numFragments; i++) { OutgoingNetMessage fmsg = m_owner.CreateOutgoingMessage(); fmsg.m_type = NetMessageLibraryType.UserFragmented; fmsg.m_msgType = NetMessageType.Data; NetBuffer fragBuf = m_owner.CreateBuffer(); fragBuf.Write(fragId); fragBuf.WriteVariableUInt32((uint)i); fragBuf.WriteVariableUInt32((uint)numFragments); if (i < numFragments - 1) { // normal fragment fragBuf.Write(data.Data, i * chunkSize, chunkSize); } else { // last fragment int bitsInLast = data.LengthBits - (chunkSize * (numFragments - 1) * 8); int bytesInLast = dataLen - (chunkSize * (numFragments - 1)); fragBuf.Write(data.Data, i * chunkSize, bytesInLast); // add receipt only to last message fmsg.m_receiptData = receiptData; } fmsg.m_data = fragBuf; fmsg.m_data.m_refCount = 1; // since it's just been created fmsg.m_numSent = 0; fmsg.m_nextResend = double.MaxValue; fmsg.m_sequenceChannel = channel; fmsg.m_sequenceNumber = -1; if (isLibraryThread) { m_unsentMessages.Enqueue(fmsg); } else { lock (m_lockedUnsentMessages) m_lockedUnsentMessages.Enqueue(fmsg); } } // TODO: recycle the original, unfragmented data return; } // // Normal, unfragmented, message // OutgoingNetMessage msg = m_owner.CreateOutgoingMessage(); msg.m_msgType = NetMessageType.Data; msg.m_type = NetMessageLibraryType.User; msg.m_data = data; msg.m_data.m_refCount++; // it could have been sent earlier also msg.m_numSent = 0; msg.m_nextResend = double.MaxValue; msg.m_sequenceChannel = channel; msg.m_sequenceNumber = -1; msg.m_receiptData = receiptData; if (isLibraryThread) { m_unsentMessages.Enqueue(msg); } else { lock (m_lockedUnsentMessages) m_lockedUnsentMessages.Enqueue(msg); } }