-
Notifications
You must be signed in to change notification settings - Fork 28
/
sender-based-controller.h
409 lines (367 loc) · 16.2 KB
/
sender-based-controller.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
/******************************************************************************
* Copyright 2016-2017 Cisco Systems, Inc. *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
* *
* You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, software *
* distributed under the License is distributed on an "AS IS" BASIS, *
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
* See the License for the specific language governing permissions and *
* limitations under the License. *
******************************************************************************/
/**
* @file
* Abstract class representing the interface to a sender-based controller
* for rmcat ns3 module.
*
* @version 0.1.1
* @author Jiantao Fu
* @author Sergio Mena
* @author Xiaoqing Zhu
*/
#ifndef SENDER_BASED_CONTROLLER_H
#define SENDER_BASED_CONTROLLER_H
#include <cstdint>
#include <string>
#include <deque>
#include <vector>
#include <tuple>
#include <utility>
namespace rmcat {
const uint32_t RMCAT_LOG_PRINT_PRECISION = 2; /* default precision for logs */
/**
* This class keeps track of the length of intervals between two packet
* loss events, in the way TCP-friendly Rate Control (TFRC) calculates it
*/
class InterLossState {
public:
InterLossState();
std::deque<uint32_t> intervals;
uint16_t expectedSeq;
bool initialized; // did the first loss happen?
};
/**
* This is the base class to all congestion controllers. Any congestion
* controller that is to use this NS3 component has to inherit from this
* class. Additionally a congestion controller needs to implement virtual
* member functions #processSendPacket , #processFeedback , #getBandwidth ,
* and (optionally) #reset . The controller's algorithm implementation is
* indeed the code behind these three member functions.
*
* As the class's name implies, the congestion control algorithm has to be
* sender-based; this means that the congestion control algorithm will be
* running exclusively at the sender endpoint. The receiver endpoint timestamps
* the arrival of media packets and sends per-packet feedback back to the
* sender endpoint.
*
* Even if this class has pure virtual methods and cannot therefore be directly
* instantiated, it contains the common basic behavior we consider all
* congestion control algorithms need:
*
* - Match send and receive timestamps of a packet and calculate its one way
* delay (OWD)
* - Calculate other packet metrics the can be useful for congestion control
* algorithms (e.g, packet loss ratio, average loss interval)
*
* We intend congestion controllers derived from this class to be independent
* from NS3 (as far as possible). The reason for this requirement is to render
* these congestion controller implementations easier to reuse in an emulated
* environment, or on a real testbed, rather than NS3. For this reason:
* - We don't follow NS3's code guidelines in these files
* - We define a logging callback, rather than directly using NS3 logging
* within the controller classes
*/
class SenderBasedController {
public:
/**
* This typedef is used to define a logging callback. For the moment,
* a simplistic logging callback will do (e.g., no logging levels)
*/
typedef void (*logCallback) (const std::string&);
/**
* This class represents an item of aggregated feedback, where the first item is the
* sequence number, the second is the receive timestamp (in microseconds), and the
* third is the ECN marking value read at the receiver
*/
struct FeedbackItem {
uint16_t sequence;
uint64_t rxTimestampUs;
uint8_t ecn;
};
/** To avoid future complexity and defects, we make the following
* assumptions regarding wrapping of unsigned integers:
* - sequences, uint16_t, can wrap (just like TCP)
* - timestamps (microseconds), uint64_t, can wrap (despite being 64 bits long)
* - delays (microseconds), uint64_t, can wrap (easily), as they are
* obtained from subtraction of timestamps obtained at different endpoints,
* which may have non-synchronized clocks.
*/
struct PacketRecord {
uint16_t sequence;
uint64_t txTimestampUs;
uint32_t size;
uint64_t owdUs;
uint64_t rttUs;
};
/** Class constructor */
SenderBasedController();
/** Class destructor */
virtual ~SenderBasedController();
/**
* Set id of the controller; it can be used to prepend the log lines
*
* @param [in] id A string denoting the flow's id
*/
void setId(const std::string& id);
/**
* Set the initial bandwidth estimation
*
* @param [in] initBw Initial bandwidth estimation
*/
void setInitBw(float initBw);
/**
* Set the minimal bandwidth. Controllers should never output
* a bandwidth smaller than this one
*
* @param [in] initBw Minimal bandwidth
*/
void setMinBw(float minBw);
/**
* Set the maximal bandwidth. Controllers should never output
* a bandwidth greater than this one
*
* @param [in] initBw Maximal bandwidth
*/
void setMaxBw(float maxBw);
/**
* Set the current bandwidth estimation. This can be useful in test environments
* to temporarily disrupt the current bandwidth estimation
*
* @param [in] newBw Bandwidth estimation to overwrite the current estimation
*/
virtual void setCurrentBw(float newBw) =0;
/**
* Set the logging callback. Congestion controllers can use this callback
* to log events occurring within the algorithm's logic
*
* @param [in] f Logging function to be called from the congestion
* controller implementation
*/
void setLogCallback(logCallback f);
/**
* This API call will reset the internal state of the congestion
* controller. The new state will be the same as that of a freshly
* instantiated controller object
*/
virtual void reset();
/**
* This function is called every time a media packet is ready to be sent
* off to the receiver endpoint. By calling this function, the sender
* application provides the congestion controller with information on the
* media packet being sent
*
* This member function is not pure virtual. All subclasses should call
* the superclass's method to ensure the proper operation of the common
* logic implemented by the superclass
*
* @param [in] txTimestampUs The time (in microseconds) at which the packet
* is sent
* @param [in] sequence The sequence number in the packet, which will be
* used to identify the corresponding feedback from
* the receiver endpoint
* @param [in] size Size of the packet in bytes. In principle, the sender
* application denotes the size of the payload (i.e.,
* without accounting for any RTP/UDP/IP overheads).
* This can be changed, though
* @retval true if all went well, false if there was an error
*
* @note There are two ways this function can fail:
* - returning false: this means that a problem was detected in
* the input parameters (e.g., bad sequence)
* - asserting (crashing): this means there is a bug in the
* function's logic
*/
virtual bool processSendPacket(uint64_t txTimestampUs,
uint16_t sequence,
uint32_t size); // in Bytes
/**
* Upon arrival of a feedback packet from the receiver endpoint, the send
* application calls this function to deliver the data contained in the
* feedback packet to the congestion controller
*
* This member function is not pure virtual. All subclasses should call
* the superclass's method to ensure the proper operation of the common
* logic implemented by the superclass
*
* @param [in] nowUs The time (in microseconds) at which this function is called
* @param [in] sequence The sequence number of the media packet that this
* feedback refers to
* @param [in] rxTimestampUs The time (in microseconds) at which this the media
* packet was received at the receiver endpoint
* @param [in] ecn The Explicit Congestion Notification (ECN) marking value
* (specified in rfc3168), as seen by the receiver endpoint
* @retval true if all went well, false if there was an error
*
* @note There are two ways this function can fail:
* - returning false: this means that a problem was detected in
* the input parameters (e.g., sequence from
* the future, decreasing timestamps)
* - asserting (crashing): this means there is a bug in the
* function's logic
*/
virtual bool processFeedback(uint64_t nowUs,
uint16_t sequence,
uint64_t rxTimestampUs,
uint8_t ecn=0);
/**
* If aggregated feedback is received from the receiver endpoint, this function
* offers the send application a way to process the aggregated feedback as a batch
*
* This member function is not pure virtual, as contains a base implementation that
* calls function #processSendPacket in a loop
*
* @param [in] nowUs The time (in microseconds) at which this function is called
* @param [in] feedbackBatch A vector of items containing sequence numbers, receive
* timestamps (in microseconds), and ECN marking values of
* the aggregated feedback
*/
virtual bool processFeedbackBatch(uint64_t nowUs,
const std::vector<FeedbackItem>& feedbackBatch);
/**
* The sender application will call this function every time it needs to
* know what is the current bandwidth as estimated by the congestion
* controller. The bandwidth information is typically used to have the
* media codecs adapt to the current (estimated) available bandwidth
*
* @param [in] nowUs The time at which this function is called, in microseconds
* @retval the congestion controller's bandwidth estimation, in bps
*/
virtual float getBandwidth(uint64_t nowUs) const =0;
protected:
/** A "less than" operator for unsigned integers that supports wrapping */
template <typename UINT>
bool lessThan(UINT lhs, UINT rhs) const {
UINT noWrapSubtract = rhs - lhs;
UINT wrapSubtract = lhs - rhs;
return noWrapSubtract < wrapSubtract;
}
/**
* Set the history length, in microseconds, for calculating metrics.
* Information from packets sent more than lenUs microseconds ago will be
* garbage collected and thus not used for metric calculation
*
* @param [in] lenUs New history length (in microseconds)
*/
void setHistoryLength(uint64_t lenUs);
/**
* Get the current history length, in microseconds. Any packet that was
* sent more than this length microseconds in the past is garbage collected
* and is not used for calculating any metric
*
* @retval The current history length (in microseconds)
*/
uint64_t getHistoryLength() const;
/**
* Function used to log messages. It calls the message logging callback
* if has been set, otherwise it logs to stdout
*/
void logMessage(const std::string& log) const;
/*
* The functions below calculate different delay and loss
* metrics based on the received feedback. Although they can
* be considered part of the NADA algorithm, we have defined
* them in the superclass because they could also be useful
* to other algorithms
* */
/*
* Calculate current queuing delay (qdelay)
*
* @param [out] qdelayUs Queuing delay in microseconds during current history length
* @retval False if the current history is empty (output parameter is not
* valid). True otherwise
*/
bool getCurrentQdelay(uint64_t& qdelayUs) const;
/**
* Calculate current round trip time (rtt)
*
* @param [out] rttUs Round trip time in microseconds during current history length
* @retval False if the current history is empty (output parameter is not
* valid). True otherwise
*/
bool getCurrentRTT(uint64_t& rttUs) const;
/**
* Calculate current info on packet losses
*
* @param [out] nLoss Number of packets lost during current history length
* @param [out] plr Loss ratio (losses per packet) for the current history
* length
* @retval False if the current history does not contain enough packets to
* calculate the metrics (output parameter is not valid). True
* otherwise
*/
bool getPktLossInfo(uint32_t& nLoss, float& plr) const;
/**
* Calculate current rate at which the receiver is receiving the media
* packets (receive rate), in bits per second
*
* @param [out] rrateBps Current receive rate in bps
* @retval False if the current history does not contain enough packets to
* calculate the metrics (output parameter is not valid). True
* otherwise
*/
bool getCurrentRecvRate(float& rrateBps) const;
/**
* Calculate the current average inter-loss interval. A loss event is
* the loss of one or more consecutive packets (loss burst). An
* inter-loss interval is the number of packets received without sequence
* gaps between two consecutive loss events. The average is weighted, based
* on the coefficients specified by the TCP-friendly rate control
* algorithm (TFRC). See rfc5348.
*
* @param [out] avgInterval Average inter-loss interval in packets
* @param [out] rrateBps Current (most recent, growing) inter-loss interval in packets
* @retval False if there have not been any losses yet, and therefore there are no
* inter-loss intervals to return; in this case, the output parameters are not
* valid). True otherwise
*/
bool getLossIntervalInfo(float& avgInterval, uint32_t& currentInterval) const;
bool m_firstSend; /**< true if at least one packet has been sent */
uint16_t m_lastSequence; /**< sequence of the last packet sent */
/**
* Estimation of the network propagation delay, plus clock difference
* between sender and receiver endpoints. In microseconds
*/
uint64_t m_baseDelayUs;
/**
* Sent packets for which feedback has not been received yet
*/
std::deque<PacketRecord> m_inTransitPackets;
/**
* Packets for which feedback has already been received. Information
* contained in these records will be used to calculate the different
* metrics that congestion controllers use
*/
std::deque<PacketRecord> m_packetHistory;
/**
* Maintains the sum of the size of all packets in #m_packetHistory .
* This is done for efficiency reasons
*/
uint32_t m_pktSizeSum;
std::string m_id; /**< Id used for logging, and can be used for plotting */
float m_initBw;
float m_minBw;
float m_maxBw;
logCallback m_logCallback;
InterLossState m_ilState;
private:
uint64_t m_historyLengthUs; // in microseconds
void setDefaultId();
void updateInterLossData(uint16_t sequence);
};
}
#endif /* SENDER_BASED_CONTROLLER_H */