| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- /*
- * 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.h"
- #if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1
- #ifndef __CONGESTION_CONTROL_SLIDING_WINDOW_H
- #define __CONGESTION_CONTROL_SLIDING_WINDOW_H
- #include "NativeTypes.h"
- #include "RakNetTime.h"
- #include "RakNetTypes.h"
- #include "DS_Queue.h"
- /// 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
|