Computer Networking - 可靠数据传输协议
《计算机网络 自顶向下方法》配套实验
实现一个可靠数据传输协议(reliable data transfer protocol),实验链接
一、比特交换协议
基于停止等待的理论,使用校验和、序号、肯定否定ACK、重传和定时器的技术,实现一个在不可靠网络上的单项数据传输的可靠协议。
停止等待:发送方在发送了一个数据包(分组)后等待接收方的回应,在此期间不发送任何数据。
序号:为了使接收方知道发送方是否在重传前一个发送分组(接收到的分组序号与最近收到的分组序号相同),在停止等待协议中,使用1比特的序号即可。
否定ACK:对上次正确接收的分组发送一个ACK(冗余ACK),即可实现否定ACK。
定时器:如果当前发送完分组后在一定时间内没有收到对应的ACK,那么进行重传。
实验框架
实验网络环境为模拟环境,分为3部分。layer5代表应用层,layer4是我们要实现的可靠数据传输层,layer3是不可靠的传输媒介。实验中实现的是单向的数据传输(A发送,B接收)
参考课本上图3-15 的发送方状态转移图和 3-14的接收方状态转移图。
应用层会调用A_outout(msg)向B发送数据,A需要构建发送分组,开启定时器。
A_input(pkt)函数用于从layer3中接收B发来的ACK信息,进行处理,判断ACK的类型和数据包是否受损。
A_timerinterrupt() 函数用于处理当前发送的分组超时后的重传。
B_input(pkt)函数从layer3中接收A发送的数据,进行处理(发送给数据接收的应用层layer5)并进行回复。
接收方和发送方的状态记录结构以及相应初始化:
const int WCALL = 0, WACK = 1;
typedef struct astate{
int state; // 当前处于等待调用 或 等待ack的状态
int curseq;
int checksum;
char data[20];
}AState;
const int WZERO = 0, WONE = 1;
typedef struct bstate{
int state;
}BState;
AState A_State;
BState B_State;
void A_init()
{
A_State.checksum = 0;
A_State.state = WCALL;
A_State.curseq = 0;
}
void B_init()
{
B_State.state = WZERO;
}
A_output
float timer_increment = 50;
int compute_pkt_sum(struct pkt packet) {
int sum = 0;
for (int i = 0; i < 20; i++ ) {
sum += packet.payload[i];
}
sum += packet.acknum;
sum += packet.seqnum;
return sum;
}
/* called from layer 5, passed the data to be sent to other side */
void A_output(message)
struct msg message;
{
if (A_State.state == WCALL) {
struct pkt pkt_to_send = {.acknum = A_State.curseq,
.seqnum = A_State.curseq};
for (int i = 0; i < 20; i++) {
char d = message.data[i];
pkt_to_send.payload[i] = d;
A_State.data[i] = d;
}
pkt_to_send.checksum = compute_pkt_sum(pkt_to_send);
A_State.state = WACK;
starttimer(A, timer_increment);
tolayer3(A, pkt_to_send);
if (TRACE > 2) {
printf("A: Being called.\n");
}
} else {
ADropped_msg++;
if (TRACE > 2) {
printf("A: Drop Message.\n");
}
}
}
A_input
/* called from layer 3, when a packet arrives for layer 4 */
void A_input(packet)
struct pkt packet;
{
if (A_State.state == WACK) {
if (packet.acknum == A_State.curseq && packet.checksum == compute_pkt_sum(packet)) {
if (TRACE > 2) {
printf("A: valid ACK.\n");
}
stoptimer(A);
A_State.curseq = !A_State.curseq;
A_State.checksum = 0;
A_State.state = WCALL;
ACKed_msg++;
} else {
printf("A: Invalid checksum or pktACK %d != curseq %d\n", packet.acknum, A_State.curseq);
}
} else {
if (TRACE > 2) {
printf("A: Invalid ACK.\n");
}
}
}
B_input
void B_input(packet)
struct pkt packet;
{
struct pkt response;
for (int i = 0; i < 20; ++i) {
response.payload[i] = 0;
}
if (packet.seqnum == B_State.state && packet.checksum == compute_pkt_sum(packet)) {
if (TRACE > 2) {
printf("B: Receive valid data. ACK%d\n", B_State.state);
}
tolayer5(B, packet.payload);
response.acknum = B_State.state;
response.seqnum = B_State.state;
response.checksum = compute_pkt_sum(response);
tolayer3(B, response);
B_State.state = !B_State.state;
} else {
if (TRACE > 2) {
printf("B: Resend ACK%d.\n", !B_State.state);
}
BACK_resend++;
// 冗余ack
response.acknum = !B_State.state;
response.seqnum = !B_State.state;
response.checksum = compute_pkt_sum(response);
tolayer3(B, response);
}
}
A_timerinterrupt
void A_timerinterrupt()
{
if (TRACE > 2) {
printf("A: time out, resend packet.\n");
}
AResend_msg++;
// resend packet
struct pkt packet = {.acknum = A_State.curseq, .checksum = A_State.checksum, .seqnum = A_State.curseq};
for (int i = 0; i < 20; i++) {
packet.payload[i] = A_State.data[i];
}
tolayer3(A, packet);
starttimer(A, timer_increment);
}
测试
使用的超时间隔是50,应用发来数据包的间隔是100。原程序中我还增加了一些记录用的变量,统计丢包率之类的。没有丢包受损的情况下,一次收发在TRACE=3的debug输出为:
EVENT time: 69.657104, type: 1, fromlayer5 entity: 0
GENERATE NEXT ARRIVAL: creating new arrival
INSERTEVENT: time is 69.657104
INSERTEVENT: future time will be 196.618607
MAINLOOP: data given to student: aaaaaaaaaaaaaaaaaaaa
START TIMER: starting timer at 69.657104
INSERTEVENT: time is 69.657104
INSERTEVENT: future time will be 119.657104
TOLAYER3: seq: 0, ack 0, check: 1940 aaaaaaaaaaaaaaaaaaaa
TOLAYER3: scheduling arrival on other side
INSERTEVENT: time is 69.657104
INSERTEVENT: future time will be 75.066002
A: Being called.
EVENT time: 75.066002, type: 2, fromlayer3 entity: 1
B: Receive valid data. ACK0
TOLAYER5: data received: aaaaaaaaaaaaaaaaaaaa
TOLAYER3: seq: 0, ack 0, check: 0
TOLAYER3: scheduling arrival on other side
INSERTEVENT: time is 75.066002
INSERTEVENT: future time will be 79.824150
EVENT time: 79.824150, type: 2, fromlayer3 entity: 0
A: valid ACK.
STOP TIMER: stopping timer at 79.824150
丢包重传的情况:
EVENT time: 286.424072, type: 1, fromlayer5 entity: 0
GENERATE NEXT ARRIVAL: creating new arrival
INSERTEVENT: time is 286.424072
INSERTEVENT: future time will be 289.861755
MAINLOOP: data given to student: cccccccccccccccccccc
START TIMER: starting timer at 286.424072
INSERTEVENT: time is 286.424072
INSERTEVENT: future time will be 336.424072
TOLAYER3: seq: 0, ack 0, check: 1980 cccccccccccccccccccc
TOLAYER3: scheduling arrival on other side
INSERTEVENT: time is 286.424072
INSERTEVENT: future time will be 293.377441
A: Being called.
EVENT time: 289.861755, type: 1, fromlayer5 entity: 0
GENERATE NEXT ARRIVAL: creating new arrival
INSERTEVENT: time is 289.861755
INSERTEVENT: future time will be 483.757172
MAINLOOP: data given to student: dddddddddddddddddddd
A: Drop Message.
EVENT time: 293.377441, type: 2, fromlayer3 entity: 1
B: Receive valid data. ACK0
TOLAYER5: data received: cccccccccccccccccccc
TOLAYER3: packet being lost
EVENT time: 336.424072, type: 0, timerinterrupt entity: 0
A: time out, resend packet.
TOLAYER3: seq: 0, ack 0, check: 0 cccccccccccccccccccc
TOLAYER3: scheduling arrival on other side
INSERTEVENT: time is 336.424072
INSERTEVENT: future time will be 343.941284
START TIMER: starting timer at 336.424072
INSERTEVENT: time is 336.424072
INSERTEVENT: future time will be 386.424072
EVENT time: 343.941284, type: 2, fromlayer3 entity: 1
B: Resend ACK0.
TOLAYER3: seq: 0, ack 0, check: 0
TOLAYER3: scheduling arrival on other side
INSERTEVENT: time is 343.941284
INSERTEVENT: future time will be 350.816040
EVENT time: 350.816040, type: 2, fromlayer3 entity: 0
A: valid ACK.
STOP TIMER: stopping timer at 350.816040
二、Go-back-N
画了接收方和发送方的有限状态机。
实现
相比比特交换协议,接收方的增加了对收到msg的缓存,这边我使用了一个循环数组来实现缓存,base、nextseqnum、tail指针将缓存分为了四个部分:已发送且收到确认的msg [0, base)
,已发送但未收到确认的msg [base, nextseqnum)
,窗口中等待发送的msg [nextseqnum, min(tail, base+WINDOW) )
, 缓存的msg [base,tail]
。
#define MAXBUFFER 50
#define WINDOW 8
typedef struct astate{
int base;
int nextseqnum;
char buffer[MAXBUFFER][20];
int tail;
int buf_size;
}AState;
int insert_tail(AState* astate, char* data) {
if (astate->buf_size == MAXBUFFER) {
return 0;
}
for (int i = 0; i < 20; ++i) {
astate->buffer[astate->tail][i] = data[i];
}
astate->tail = (astate->tail + 1) % MAXBUFFER;
astate->buf_size++;
return 1;
}
typedef struct bstate{
int expectedseqnum;
}BState;
具体实现时注意缓存是循环使用的,使用buf_size记录已经缓存的数据量,完整代码链接
测试
设置的超时为100,消息间隔为50,丢包率为0.1,受损率为0.1,总共发送10个消息,TRACE=2。
正常收发
EVENT time: 34.828552, type: 1, fromlayer5 entity: 0
A: Got Message ,window [0, 0)
EVENT time: 40.237450, type: 2, fromlayer3 entity: 1
B: ACK 0
EVENT time: 44.995598, type: 2, fromlayer3 entity: 0
A: Receive valid ACK, window updated:[1, 1)
EVENT time: 98.309303, type: 1, fromlayer5 entity: 0
A: Got Message ,window [1, 1)
EVENT time: 107.837433, type: 2, fromlayer3 entity: 1
B: ACK 1
EVENT time: 117.540672, type: 2, fromlayer3 entity: 0
A: Receive valid ACK, window updated:[2, 2)
丢包
A: Got Message ,window [2, 2)
EVENT time: 153.000458, type: 2, fromlayer3 entity: 1
B: ACK 2
TOLAYER3: packet being lost
EVENT time: 238.246262, type: 1, fromlayer5 entity: 0
A: Got Message ,window [2, 3)
EVENT time: 243.212036, type: 0, timerinterrupt entity: 0
A: time out. Go back N.[2, 4)
A: Resend seq 2
TOLAYER3: packet being lost
A: Resend seq 3
EVENT time: 244.415878, type: 2, fromlayer3 entity: 1
B: ACK 3
EVENT time: 251.290649, type: 2, fromlayer3 entity: 0
A: Receive valid ACK, window updated:[4, 4)
重传
A: time out. Go back N.[4, 6)
A: Resend seq 4
A: Resend seq 5
EVENT time: 426.913330, type: 2, fromlayer3 entity: 1
B: ACK 5
TOLAYER3: packet being corrupted
EVENT time: 430.196320, type: 2, fromlayer3 entity: 1
B: resend ACK 4
EVENT time: 432.601593, type: 2, fromlayer3 entity: 1
B: resend ACK 5
TOLAYER3: packet being lost
EVENT time: 433.927460, type: 2, fromlayer3 entity: 0
A: Invalid packet data.
EVENT time: 441.234406, type: 2, fromlayer3 entity: 0
Warning: attempt to start a timer that is already started
A: Receive valid ACK, window updated:[5, 6)