There are no mechanisms to guarantee that cumulative byte count are correctly synchronized between source and receiver i.e., under atypical conditions, they may have different ideas about when the "connection" began. For example, a system reset may cause cumulative counts to be reset to 0 on one machine without the other party recognizing it as a new connection. These events should be rare, but care must be taken to insure that their impact is minimized, by basing calculations only upon byte count differences between successive reports. This will be an accurate measure of what has occured in the preceding interval, except in the case where an abnormal reset has occurred within that interval (in which case a negative byte count difference would usually identify the value as bogus).
A Rate Control packet includes the local system time that it was generated, which is echoed back in the Rate Reply packet to simplify round trip time calculation. A Rate Reply packets also contains the local system time that it was generated, enabling the data transmitter to monitor variation in one-way transmission latency. All times are expressed in milliseconds, with respect to arbitrary local origins.
A Rate Control packet includes the transmission rate cap which the data transmitter is currently using. This can be used for informative display. A Rate Reply packets contains a reception rate cap, representing the highest data rate that is desired. Data transmitters are required to respect this cap, even if the connection appears able to support higher rates.
In the current implementation, a data transmitter sends a Rate Control packet to each next hop recipient at 3 second intervals. Transmission rates are adjusted whenever a Rate Reply packet arrives, provided that at least 1 second has elapsed since the last adjustment. Adjustments are based on the packet loss observed during the preceding interval. Timing information is not used in current implementations.
The reflector will generate Rate Control packets only if it determines that the client is able to respond to them. Clients are assumed capable if and only if they include RateReport data in their OpenContinue packets. If not, the reflector uses an alternative rate adjustment, based on end-to-end loss reported in OpenContinue packets. Clients always generate Rate Control packets whether or not the recipient is compatible. If no Rate Reply packets are received, clients use an alternative mechanism for adjusting their data rate, based on end-to-end loss reported in OpenContinue packets.
0 8 16 24 31 +--------------+--------------+--------------+--------------+ | CU-SeeMe Header | | (26 bytes) | | ... | | +--------------+--------------+ | | Send Cap | +--------------+--------------+--------------+--------------+ 1Ch | Total Bytes Sent | +--------------+--------------+--------------+--------------+ 20h | Time Sent | +--------------+--------------+--------------+--------------+00h - CU-SeeMe Header (26 bytes)
0 8 16 24 31 +--------------+--------------+--------------+--------------+ | CU-SeeMe Header | | (26 bytes) | | ... | | +--------------+--------------+ | | Recv Cap | +--------------+--------------+--------------+--------------+ 1Ch | Rate Control SeqNum | +--------------+--------------+--------------+--------------+ 20h | Total Bytes Sent | +--------------+--------------+--------------+--------------+ 24h | Total Bytes Recv | +--------------+--------------+--------------+--------------+ 28h | Time Sent | +--------------+--------------+--------------+--------------+ 2Ch | Time Recv | +--------------+--------------+--------------+--------------+00h - CU-SeeMe Header (26 bytes)
void DoRateReplyPacket(RateReplyPacket *rRP) { long bytesSent,bytesRecv,sentKbps,percentRecv,cap,mSec,eMSec,bytes; // receipt of RateReply indicates version 2 Rate Control gSendRate->version = 2; // most link wishes to receive gMaxRecvKbps = rRP->rateReply.maxRecvKbps; // calculate bytes sent since last RateReply packet bytes = rRP->rateReply.totalBytesSentToMe; bytesSent = bytes - gSendRate->replyBytesSent; gSendRate->replyBytesSent = bytes; // calculate bytes received since last RateReply packet bytes = rRP->rateReply.totalBytesRecvByMe; bytesRecv = bytes - gSendRate->replyBytesRecv; gSendRate->replyBytesRecv = bytes; // calculate elapsed time since last RateReply packet mSec = GetMSec(); eMSec = mSec - gSendRate->replyTime; gSendRate->replyTime = mSec; // don't adjust unless at least 1 second has elapsed if (eMSec <= 1000) return; cap = gKbpsRate; // the current transmission rate cap // absurdities may occur as a result of mis-initialization // or other rare events if( (bytesSent > 0) && (bytesRecv >= 0) && (bytesRecv <= bytesSent) ) { // values are reasonable percentRecv = (bytesRecv * 100) / bytesSent; sentKbps = (bytesSent << 3) / eMSec; // set a flag if we have experienced loss above threshold if( percentRecv < gSendRate->lossThreshold ) gSendRate->noLossYet = 0; // do not adjust cap if actual transmission rate was less // than 90% of cap and we succeeded at that rate if( !( (100 * sentKbps < 90 * cap) && (percentRecv > gSendRate->lossThreshold)) ) { // adjust transmission rate if( percentRecv < 75 ) percentRecv = 75; else if( percentRecv == 100 ) { // add growth rate for no loss interval percentRecv += gSendRate->noLossGrowth; // 20% extra growth if no loss has been observed yet if (gSendRate->noLossYet) percentRecv += 20; } // calculate new cap cap = ( percentRecv * (cap + 2) + 50 ) / gSendRate->lossThreshold; } } // if cap has adapted OR RateReply requests new maxRecvKbps... BW_SetRateCap(cap); }