public bool GetUDPMessage(out ByteArray sendMessage) { sendMessage = null; //Not enough data to send a packet if (!uploadBucket.TestBytes(500)) { return(false); } long currentTime = DateTime.UtcNow.Ticks; //Let the other side know they can stop sending the rate setup message if (sendRateAcknowledge) { sendRateAcknowledge = false; sendMessage = GetRateAcknowledge(); return(true); } //Connection setup, prevents the other side from sending too fast if (!receivedRateMessage && currentTime > nextRateMessageTime) { nextRateMessageTime = currentTime + TimeSpan.TicksPerSecond; sendMessage = GetRate(); return(true); } //If we have more than 1 second of data to send, or our bucket is low, let's skip the double send double uploadMaxed = uploadBucket.bucketBytes / (double)uploadBucket.bucketMax; bool efficencyMode = queuedBytes > uploadBucket.bucketMax || uploadMaxed < 0.5d; //Retransmit half as often long efficencyOffset = 0; if (efficencyMode) { efficencyOffset = TimeSpan.TicksPerMillisecond * settings.retransmit; } OutgoingMessage om; //Send retransmits if (sendMessage == null && udpRetransmitSendQueue.TryPeek(out om)) { if (currentTime > om.sendTime + efficencyOffset) { bool skipping = true; while (skipping) { if (udpRetransmitSendQueue.TryDequeue(out om)) { //Don't send messages they have acknowledged if (AckGreaterThan(om.sequence, sendAck)) { //Send back to the retransmit queue om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); //Send sendMessage = om.data; break; } else { Recycler.Release(om.data); } } else { skipping = false; } } } } //Send double sends if (sendMessage == null) { bool skipping = true; while (skipping) { if (udpDoubleSendQueue.TryPeek(out om)) { //This isn't ready to transmit yet. if (om.sendTime > currentTime + efficencyOffset) { break; } } if (udpDoubleSendQueue.TryDequeue(out om)) { if (AckGreaterThan(om.sequence, sendAck)) { //Send to the retransmit queue om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); //Send sendMessage = om.data; break; } else { //Already ACK'd, skip it Recycler.Release(om.data); } } else { skipping = false; } } } //Take new waiting data and build a message if we have less than 10000 queue'd packets int ackDiff = sendSequence - sendAck; if (ackDiff < 0) { ackDiff = ackDiff + ushort.MaxValue; } if (sendMessage == null && ackDiff < 10000 && udpSendQueue.Count > 0) { ByteArray payload = Recycler.Grab(); ByteArray addMessage; //Grab upto 500 bytes while (udpSendQueue.TryPeek(out addMessage)) { if (payload.length + addMessage.length > 500) { break; } else { udpSendQueue.TryDequeue(out addMessage); Interlocked.Add(ref queuedBytes, -addMessage.length); Array.Copy(addMessage.data, 0, payload.data, payload.length, addMessage.length); payload.length += addMessage.length; Recycler.Release(addMessage); } } om = new OutgoingMessage(); om.sequence = sendSequence++; om.data = GetUDPMessageData(1, om.sequence, payload); //Send it to the correct retransmit queue if (!efficencyMode && settings.initialRetransmit > 0) { om.sendTime = currentTime + (settings.initialRetransmit * TimeSpan.TicksPerMillisecond); udpDoubleSendQueue.Enqueue(om); } else { om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); } //Send sendMessage = om.data; statistics.sentUniquePackets++; statistics.sentUniqueBytes += payload.length; } //Send heartbeats if (sendMessage == null && currentTime > sendTime) { sendMessage = GetHeartbeat(); } //Send if (sendMessage != null) { sendTime = currentTime + (TimeSpan.TicksPerMillisecond * 100); //Update ACK WriteInt16(receiveSequence - 1, sendMessage.data, 14); while (!uploadBucket.RequestBytes(sendMessage.length)) { Thread.Sleep(1); } return(true); } return(false); }
public bool GetUDPMessage(out ByteArray sendMessage) { sendMessage = null; //Not enough data to send a packet if (!uploadBucket.TestBytes(500)) { return(false); } long currentTime = DateTime.UtcNow.Ticks; //Send retransmits OutgoingMessage om; if (udpDoubleSendQueue.TryPeek(out om)) { if (currentTime > om.sendTime) { if (udpDoubleSendQueue.TryDequeue(out om)) { //Send to the retransmit queue om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); //Send sendMessage = om.data; } } } if (sendMessage == null && udpRetransmitSendQueue.TryPeek(out om)) { if (currentTime > om.sendTime) { bool skipping = true; while (skipping) { if (udpRetransmitSendQueue.TryDequeue(out om)) { //Don't send messages they have acknowledged if (AckGreaterThan(om.sequence, sendAck)) { //Send back to the retransmit queue om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); //Send sendMessage = om.data; break; } else { Recycler.Release(om.data); } } else { skipping = false; } } } } //Take new waiting data and build a message if we have less than 1000 queue'd packets int ackDiff = sendSequence - sendAck; if (ackDiff < 0) { ackDiff = ackDiff + ushort.MaxValue; } if (sendMessage == null && ackDiff < 1000 && udpSendQueue.Count > 0) { ByteArray payload = Recycler.Grab(); ByteArray addMessage; //Grab upto 500 bytes while (udpSendQueue.TryPeek(out addMessage)) { if (payload.length + addMessage.length > 500) { break; } else { udpSendQueue.TryDequeue(out addMessage); Array.Copy(addMessage.data, 0, payload.data, payload.length, addMessage.length); payload.length += addMessage.length; Recycler.Release(addMessage); } } om = new OutgoingMessage(); om.sequence = sendSequence++; om.data = GetUDPMessageData(1, om.sequence, payload); //Send it to the correct retransmit queue if (settings.initialRetransmit > 0) { om.sendTime = currentTime + (settings.initialRetransmit * TimeSpan.TicksPerMillisecond); udpDoubleSendQueue.Enqueue(om); } else { om.sendTime = currentTime + (settings.retransmit * TimeSpan.TicksPerMillisecond); udpRetransmitSendQueue.Enqueue(om); } //Send sendMessage = om.data; } //Send heartbeats if (sendMessage == null && currentTime > sendTime) { sendMessage = GetHeartbeat(); } if (sendMessage != null) { sendTime = currentTime + (TimeSpan.TicksPerMillisecond * 100); //Update ACK WriteInt16(receiveSequence - 1, sendMessage.data, 14); while (!uploadBucket.RequestBytes(sendMessage.length)) { Thread.Sleep(1); } return(true); } return(false); }