网络原理自顶向下三可靠数据传输原理实现GBN滑动窗口协议
在GBN中,允许发送方发送多个分组而不需等待确认,但它也受限于在流水线中未确认的分组数不能超过某个最大允许数N。
我们需要
- base 基序号 最早未确认分组的序号
- nextSeqNum下一个序号 最小的未使用序号
则可以将序号范围分割成四段
- 0 至 base - 1 已经发送并被确认的分组
- base 至 nextSeqNum - 1 已经发送没有被确认的分组
- nextSeqNum 至 base + N - 1 可以被发送的分组序号
- base + N 至 无穷大 不能使用的序号
已经被发送的可以但未被确认的可以看成是一个在序号范围内长度为N的窗口。随着协议允许,窗口往前滑动。因此N常被成为窗口长度
,GBN协议也被称为滑动窗口协议
发送端
GBN的发送方必须响应三种类型的事件
- 上层的调用。检测有没有可以使用的序号,如果有就发送。
- 收到ACK。GBN使用
累计确认
。即收到的N之前的全部确认。
- 超时事件。如果出现超时,就重传所有已发送未确认的分组。
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
| $base = 1; $nextSeqNum = 1; function rdt_send($data) { $checkSum = generateCheckSum($data); $packet[$nextSeqNum] = make_pkt($data, $checkSum, $nextSeqNum); udt_send($packet[$nextSeqNum]); if ($base == $nextSeqNum) { start_timer(); } if (timeout()) { for ($i = $base; $i <= $nextSeqNum - 1; $i++) { udt_send($packet[$i]); } } else if ($isAck = rdt_rev() && check($isAck)) { $ackNum = getAckNum($isAck); $base = $ackNum; if ($base == $nextSeqNum) { stop_timer(); } else { start_timer(); } } }
|
接收端
GBN的接收端较为简单,因为接收端只需要按序号交付数据就可以了。如果数据没有按序到达接收端,接收端只需要直接丢弃,因为发送端会重传所有分组。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| $expackNum = 1; function rdt_rev($packet) { if (check($packet) && $packet['num'] == $expackNum) { $data = extract($packet); deliver_data($data); $ack = make_pkt(1, $expackNum); $expackNum++; udt_send($ack); } else { $ack = make_pkt(1, $expackNum); udt_send($ack); } }
|
SR
在SR中,和GBN不同,SR是给每一个分组设置定时器,发送端只确认重传当前分组,而不是所有分组。接收端在接收到乱序的分组的时候会进行缓存,当前面的分组到达以后一起提交给应用层。
发送端
- 等待上层调用。这里和GBN一样
- 超时。超时哪个重传哪个
- 收到ACK。如果收到的是最小序号的ACK,那么base可以往前移动,也就是窗口滑动。如果收到其他序号的ACK。那么把这些ACK缓存。
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
| $base = 1; $nextSeqNum = 1; function rdt_send($data) { $checkSum = generateCheckSum($data); $packet[$nextSeqNum] = make_pkt($data, $checkSum, $nextSeqNum); udt_send($packet[$nextSeqNum]); start_timer($nextSeqNum); if ($timeNum = timeout()) { udt_send($packet[$timeNum]); } else if ($isAck = rdt_rev() && check($isAck)) { $ackNum = getAckNum($isAck); stop_timer($ackNum); if ($ackNum == $base) { ++$base; while(array_key_exists(++$ackNum, $cache)) { ++$base; } } else { $cache[$ackNum] = $ackNum; } } }
|
接收端
- 序号在rcv_base 至 rcv_base + N - 1内的分组被正确接受。如果该分组不是期望的分组,那么缓存,如果是,那么给应用层并且看缓存里面有没有后续,有就直接一起给应用层
- 序号在rcv_base - N 至 rcv_base - 1内的分组被正确接受。返回一个确认ACK。表示我已经收到了。
- 其他情况。忽略分组
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
| $expackNum = 1; function rdt_rev($packet) { if (check($packet)) { if ($packet['num'] > $expackNum && $packet['num'] < $expackNum + N - 1) { if ($packet['num'] == $expackNum) { $data = extract($packet); deliver_data($data); $ack = make_pkt(1, $expackNum); $expackNum++; udt_send($ack); $key = $expackNum + 1; while(array_key_exists($key, $cache)) { deliver_data($cache[$expackNum]); ++$expackNum; $key++; } } else { $cache[$packet['num']] = $packet; $ack = make_pkt(1, $packet['num']); udt_send($ack); } } } else { $ack = make_pkt(1, $expackNum); udt_send($ack); } }
|