不得不惊讶于前人的智慧和protocal6实现代码中大量巧妙地处理技巧。这部分代码是《Computer Networks, Andrew S.Tanenbaum (5th)》中对选择重传协议的实现代码的描述,其中充斥了大量可供学习、聪明的处理方法。能从头到尾彻彻底底地分析代码的实现机制,不仅有利于提高对protocol6的理解,还能很好地学习到前人设计协议、实现协议的智慧。
/* Protocol 6 (Selective repeat) accepts frames out of order but passes packets to the
network layer in order. Associated with each outstanding frame is a timer. When the timer
expires, only that frame is retransmitted, not all the outstanding frames, as in protocol 5. */
/* Sender and receiver are the same */
/* 关键细节
*
* 1.data,nak都可以携带ack,且每次发送data nak都会携带一个ack,这就意味着会发送重复确认
*
* 2.receiver收到frame_expected对应的data_frame后,先前移下沿窗口,再发ack(捎带确认)
* 故当发送捎带确认时,ack的序列号总是当前frame_expected的前一个序列号
*
* 3.ATTENTION!!!!!
* 双方是等价的,都各有一个发送窗口和接收窗口,所以双方既是sender也是receiver
* 代码中的sender和receiver都是相对于本方而言
*
* 4.buffer大小
* (MAX_SEQ+1)>>1
*
* 5.序列号在buffer内定位
* frame_nr % NR_BUFS
*
* 6.ack序列号为frame_expected的上一个序列号
* (frame_expected + MAX_SEQ) % (MAX_SEQ + 1);
*
* 7.请求sender重传的data frame的序列号=frame_expected, 6中得到的ack序列号的下一个
* (r.ack+1)%(MAX_SEQ+1)
*/
#define MAX_SEQ 7 /* should be 2ˆn − 1 */
#define NR_BUFS ((MAX_SEQ + 1)/2) //4
typedef enum {
frame_arrival, cksum_err, timeout, network_layer_ready, ack_timeout} event_type;
#include "protocol.h"
boolean no nak = true; /* no nak has been sent yet */
seq_nr oldest_frame = MAX_SEQ + 1; /* initial value is only for the simulator */
//8
/*
* -:表示该区间是窗口区间
* [0...a---b---c...7,0]:(a <= b) && (b < c)
* [0---b---c...a---0]:(b < c) && (c < a)
* [0---c...a---b---0]:(c < a) && (a <= b)
*/
//[a,c) 前闭后开区间 常用区间表示 STL常见到此类区间表示方法
static boolean between(seq_nr a, seq_nr b, seq_nr c)//已经有前提保证b>0
{
/* Same as between in protocol 5, but shorter and more obscure. */
return ((a <= b) && (b < c)) || ((c < a) && (a <= b)) || ((b < c) && (c < a));
}
/*
* frame_expected对所有种类的frame一视同仁,都是当前本方receiver窗口下沿对应的窗口
* 接收方的frame_expected理论上的确不应该被其它任何值的改变所影响
*
* frame_kind=data, 则frame_nr=data frame的序列号,frame_expected是用于计算获取s.ack(frame_expected的上一个=s.ack)
* 第四个参数是out_buf
*
* s.fk=data, s.seq=frame_nr, s.ack, s.info
*
* frame_kind=nak, 则frame_nr无效(默认为0), frame_expected用于计算s.ack, buffer为out_buf。
* s.fk=nak, s.seq=0, s.ack, s.info
* 注意:此时的s.ack既有帮助处理nak也有捎带确认的功能(双重功效)
*
* 发送nak的条件:nak尚未发出,此时receiver收到的data frame != frame_expected(窗口下沿)或checksum错误
* 接收处理nak的语句只有一个,即对上述两种需要发送nak的情况都用一块语句来接收处理。
*
* 情况1:receiver收到的data frame != frame_expected(窗口下沿)
* 处理办法:假定对方sender窗口是[0 1 2],本方receiver窗口是[0 1 2],若对方sender发送0,本方sender回送的ack丢失,
* 但此时本方receiver窗口已经是[1 2 3],对方会重发0,此时触发本方sender发送nak的情况1。nak的唯一实用参数是
* 本方frame_expected,在send_frame()中,s.ack=本方frame_expected的上一个,
* 在对方接受处理nak的语句中 if(r.kind==nak),(r.ack+1)%(MAX_SEQ+1)=本方frame_expected=1
*