libnative-utilities/include/raknet/CCRakNetSlidingWindow.hpp
2024-08-15 18:40:30 +08:00

221 lines
8.4 KiB
C++

/*
* Copyright (c) 2014, Oculus VR, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
/*
http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html
cwnd=max bytes allowed on wire at once
Start:
cwnd=mtu
ssthresh=unlimited
Slow start:
On ack cwnd*=2
congestion avoidance:
On ack during new period
cwnd+=mtu*mtu/cwnd
on loss or duplicate ack during period:
sshtresh=cwnd/2
cwnd=MTU
This reenters slow start
If cwnd < ssthresh, then use slow start
else use congestion avoidance
*/
#include "RakNetDefines.hpp"
#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1
#ifndef __CONGESTION_CONTROL_SLIDING_WINDOW_H
#define __CONGESTION_CONTROL_SLIDING_WINDOW_H
#include "NativeTypes.hpp"
#include "RakNetTime.hpp"
#include "RakNetTypes.hpp"
#include "DS_Queue.hpp"
/// Sizeof an UDP header in byte
#define UDP_HEADER_SIZE 28
#define CC_DEBUG_PRINTF_1(x)
#define CC_DEBUG_PRINTF_2(x,y)
#define CC_DEBUG_PRINTF_3(x,y,z)
#define CC_DEBUG_PRINTF_4(x,y,z,a)
#define CC_DEBUG_PRINTF_5(x,y,z,a,b)
//#define CC_DEBUG_PRINTF_1(x) printf(x)
//#define CC_DEBUG_PRINTF_2(x,y) printf(x,y)
//#define CC_DEBUG_PRINTF_3(x,y,z) printf(x,y,z)
//#define CC_DEBUG_PRINTF_4(x,y,z,a) printf(x,y,z,a)
//#define CC_DEBUG_PRINTF_5(x,y,z,a,b) printf(x,y,z,a,b)
/// Set to 4 if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0
#define CC_TIME_TYPE_BYTES 8
#if CC_TIME_TYPE_BYTES==8
typedef RakNet::TimeUS CCTimeType;
#else
typedef RakNet::TimeMS CCTimeType;
#endif
typedef RakNet::uint24_t DatagramSequenceNumberType;
typedef double BytesPerMicrosecond;
typedef double BytesPerSecond;
typedef double MicrosecondsPerByte;
namespace RakNet
{
class CCRakNetSlidingWindow
{
public:
CCRakNetSlidingWindow();
~CCRakNetSlidingWindow();
/// Reset all variables to their initial states, for a new connection
void Init(CCTimeType curTime, uint32_t maxDatagramPayload);
/// Update over time
void Update(CCTimeType curTime, bool hasDataToSendOrResend);
int GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend);
int GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend);
/// Acks do not have to be sent immediately. Instead, they can be buffered up such that groups of acks are sent at a time
/// This reduces overall bandwidth usage
/// How long they can be buffered depends on the retransmit time of the sender
/// Should call once per update tick, and send if needed
bool ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick);
/// Every data packet sent must contain a sequence number
/// Call this function to get it. The sequence number is passed into OnGotPacketPair()
DatagramSequenceNumberType GetAndIncrementNextDatagramSequenceNumber(void);
DatagramSequenceNumberType GetNextDatagramSequenceNumber(void);
/// Call this when you send packets
/// Every 15th and 16th packets should be sent as a packet pair if possible
/// When packets marked as a packet pair arrive, pass to OnGotPacketPair()
/// When any packets arrive, (additionally) pass to OnGotPacket
/// Packets should contain our system time, so we can pass rtt to OnNonDuplicateAck()
void OnSendBytes(CCTimeType curTime, uint32_t numBytes);
/// Call this when you get a packet pair
void OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime);
/// Call this when you get a packet (including packet pairs)
/// If the DatagramSequenceNumberType is out of order, skippedMessageCount will be non-zero
/// In that case, send a NAK for every sequence number up to that count
bool OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount);
/// Call when you get a NAK, with the sequence number of the lost message
/// Affects the congestion control
void OnResend(CCTimeType curTime, RakNet::TimeUS nextActionTime);
void OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber);
/// Call this when an ACK arrives.
/// hasBAndAS are possibly written with the ack, see OnSendAck()
/// B and AS are used in the calculations in UpdateWindowSizeAndAckOnAckPerSyn
/// B and AS are updated at most once per SYN
void OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber );
void OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber );
/// Call when you send an ack, to see if the ack should have the B and AS parameters transmitted
/// Call before calling OnSendAck()
void OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS);
/// Call when we send an ack, to write B and AS if needed
/// B and AS are only written once per SYN, to prevent slow calculations
/// Also updates SND, the period between sends, since data is written out
/// Be sure to call OnSendAckGetBAndAS() before calling OnSendAck(), since whether you write it or not affects \a numBytes
void OnSendAck(CCTimeType curTime, uint32_t numBytes);
/// Call when we send a NACK
/// Also updates SND, the period between sends, since data is written out
void OnSendNACK(CCTimeType curTime, uint32_t numBytes);
/// Retransmission time out for the sender
/// If the time difference between when a message was last transmitted, and the current time is greater than RTO then packet is eligible for retransmission, pending congestion control
/// RTO = (RTT + 4 * RTTVar) + SYN
/// If we have been continuously sending for the last RTO, and no ACK or NAK at all, SND*=2;
/// This is per message, which is different from UDT, but RakNet supports packetloss with continuing data where UDT is only RELIABLE_ORDERED
/// Minimum value is 100 milliseconds
CCTimeType GetRTOForRetransmission(unsigned char timesSent) const;
/// Set the maximum amount of data that can be sent in one datagram
/// Default to MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE
void SetMTU(uint32_t bytes);
/// Return what was set by SetMTU()
uint32_t GetMTU(void) const;
/// Query for statistics
BytesPerMicrosecond GetLocalSendRate(void) const {return 0;}
BytesPerMicrosecond GetLocalReceiveRate(CCTimeType currentTime) const;
BytesPerMicrosecond GetRemoveReceiveRate(void) const {return 0;}
//BytesPerMicrosecond GetEstimatedBandwidth(void) const {return B;}
BytesPerMicrosecond GetEstimatedBandwidth(void) const {return GetLinkCapacityBytesPerSecond()*1000000.0;}
double GetLinkCapacityBytesPerSecond(void) const {return 0;}
/// Query for statistics
double GetRTT(void) const;
bool GetIsInSlowStart(void) const {return IsInSlowStart();}
uint32_t GetCWNDLimit(void) const {return (uint32_t) 0;}
/// Is a > b, accounting for variable overflow?
static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b);
/// Is a < b, accounting for variable overflow?
static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b);
// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond);
uint64_t GetBytesPerSecondLimitByCongestionControl(void) const;
protected:
// Maximum amount of bytes that the user can send, e.g. the size of one full datagram
uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER;
double cwnd; // max bytes on wire
double ssThresh; // Threshhold between slow start and congestion avoidance
/// When we get an ack, if oldestUnsentAck==0, set it to the current time
/// When we send out acks, set oldestUnsentAck to 0
CCTimeType oldestUnsentAck;
CCTimeType GetSenderRTOForACK(void) const;
/// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment
DatagramSequenceNumberType nextDatagramSequenceNumber;
DatagramSequenceNumberType nextCongestionControlBlock;
bool backoffThisBlock, speedUpThisBlock;
/// Track which datagram sequence numbers have arrived.
/// If a sequence number is skipped, send a NAK for all skipped messages
DatagramSequenceNumberType expectedNextSequenceNumber;
bool _isContinuousSend;
bool IsInSlowStart(void) const;
double lastRtt, estimatedRTT, deviationRtt;
};
}
#endif
#endif