Rate Control Packets

Overview

Each data transmitter, either an originating source, or a forwarding reflector, monitors the reception success of each next hop recipient, and scales the rate of its transmissions to that recipient accordingly. To monitor reception success, a Rate Control packet, containing a cumulative count of the bytes sent so far, is periodically injected into the outgoing data stream. This triggers the recipient to respond immediately with a Rate Reply packet, which contains a cumulative count of the bytes received so far. This exchange allows the data transmitter to detect loss, and also sample round trip latency. The cumulative sent value from the triggering Rate Control packet is echoed back in the Rate Reply packet in order to eliminate ambiguity about which Rate Control packet it is a reply to.

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.

Rate Control Packet Format

    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)
With Data Type = kRateControlType = 110

1Ah - Send Cap (2 bytes - unsigned integer)
The current transmission rate cap on the link from the RateControl source to the RateControl recipient in kilobits/second (1000 bits per kilobit).

1Ch - Total Bytes Sent (4 bytes - unsigned integer)
A cumulative count of the total number of bytes sent since connection initiation, up to, and including, this packet. Byte counts include all packet payloads plus 28 bytes/packet assumed for IP and UDP headers.

20h - Time Sent (4 bytes - unsigned integer)
The time at which this packet was sent, expressed in milliseconds, with respect to an arbitrary local origin.

Rate Reply Packet Format

    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)
With Data Type = kRateReplyType = 111

1Ah - Recv Cap (2 bytes - unsigned integer)
The maximum reception rate currently desired by the RateReply source, for data transmitted to it from the RateReply destination, in kilobits/second.

1Ch - Rate Control SeqNum (4 bytes - unsigned integer)
The header.seqNum of the RateControl packet to which this packet is a reply (obsolete).

20h - Total Bytes Sent (4 bytes - unsigned integer)
The value of the Total Bytes Sent field from the RateControl packet to which this packet is a reply.

24h - Total Bytes Recv (4 bytes - unsigned integer)
A cumulative count of the total number of bytes received, since connection initiation, up to, and including, the RateControl packet to which this packet is a reply.

28h - Time Sent (4 bytes - unsigned integer)
The value of the Time Sent field from the RateControl packet to which this packet is a reply.

2Ch - Time Recv (4 bytes - unsigned integer)
The time at which the Rate Control packet to which this packet is a reply was received, expressed in milliseconds, with respect to an arbitrary local origin.

Rate Adjustment Algorithm

The following code from the Macintosh version illustrates the processing of Rate Reply packets and the adjustment of outgoing transmission rates. Equivalent processing is performed by the reflector and Windows versions.
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);
}