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
|
# RFC 6298's retransmission timer in plain Python: the reference 140 counts aup's lines against. A sender's segments
# are sent and acknowledged (cumulatively) at given times; the round-trip time of each acknowledged segment that was
# never sent again (Karn) updates SRTT, RTTVAR and RTO (2.2-2.5: 1 s at least, 60 s at most), and the timer runs as
# 5.1-5.6 say: started when one is sent and none runs, stopped when all are acknowledged, restarted when new data is,
# and on expiry the earliest is sent again and RTO doubles. The timer is seen at ticks of period P, as an app sees time.
LOWEST, HIGHEST = 1000, 60000 # milliseconds; the clock's granularity G is the tick's period
class Sender:
def __init__(self, period):
self.period, self.srtt, self.rttvar, self.rto, self.measured = period, 0.0, 0.0, 1000.0, False
self.deadline, self.outstanding, self.retransmits = -1.0, [], [] # outstanding: [seq, sent, again]
def send(self, now, seq):
self.outstanding.append([seq, now, False])
if self.deadline < 0:
self.deadline = now + self.rto # 5.1
def ack(self, now, seq):
acked = [s for s in self.outstanding if s[0] <= seq]
if not acked:
return
self.outstanding = [s for s in self.outstanding if s[0] > seq]
last = acked[-1]
if not last[2]: # Karn: no sample from a segment sent again
r = now - last[1]
if not self.measured:
self.srtt, self.rttvar, self.measured = r, r / 2, True # 2.2
else:
self.rttvar = 0.75 * self.rttvar + 0.25 * abs(self.srtt - r) # 2.3
self.srtt = 0.875 * self.srtt + 0.125 * r
self.rto = min(max(self.srtt + max(self.period, 4 * self.rttvar), LOWEST), HIGHEST) # 2.4, 2.5
self.deadline = now + self.rto if self.outstanding else -1.0 # 5.3, 5.2
def tick(self, now):
if self.deadline >= 0 and now >= self.deadline: # 5.4-5.6
self.outstanding[0][2] = True
self.retransmits.append((self.outstanding[0][0], now))
self.rto = min(self.rto * 2, HIGHEST)
self.deadline = now + self.rto
|