实验三 编程实现可靠数据传输原理Go-Back-N
Go-Back-N 的有限状态机模型表示如图所示:
实验设置:
编程语言:python3
实验设置:
- 设置发送方和接收方
- 双方建立连接握手后,发送方向接收方发送多条数据
本实验中:
messages = '123,456,654,788,87364,875,8374' # 可传输多条信息,每条信息以逗号隔开,每条信息分别依次发送
- 发送方向接收方传输数据时,按一定概率发生丢包,本实验设置丢包概率20%。(lost_probability = 0.2)
- 设置接收方向发送方发送的回应不会丢失
- 对滑窗的第一个窗口,也就是最早发送的且没有ack的数据包设置时钟,计时到之后会重滑窗的所有数据包。
- 接收方采用累计确认方法
实验文件结构:
实验步骤:
Sender.py 关键代码:
发送消息:
# 尝试发送消息
if(nextseqnum < base + window_size): # 是否可以发送这个消息
# 发送消息
chksum_s = Mychecksum(str(nextseqnum) + message_list[nextseqnum-1])
sndpkt = str(nextseqnum) + ',' + message_list[nextseqnum-1] + ',' + chksum_s # 打包数据nextseqnum,message,chksum
print('发送消息:', sndpkt)
conn.send(sndpkt.encode())
if base == nextseqnum: # 发送的消息是滑窗的第一个窗口,开启计时
stop_timer = 0
pickle.dump(0, open('flag', 'wb'))
timer1 = threading.Thread(target=mytimer)
timer1.start()
print("开始计时")
message_flag[nextseqnum-1] = 1 # 发送过的消息相应的flag置为1
nextseqnum = nextseqnum + 1
time.sleep(1)
接收响应:
# 接收消息
try:
receive = conn.recv(1024).decode()
if receive == 'Lost': # 发生丢包,接收端没有收到消息,但发送端并不知道,因此不做任何动作
has_receive = False
else:
has_receive = True
except:
pass
if has_receive: # 接收到接收端的回应
print('接收消息:', receive)
acknum, expectseqnum, chksum_r = receive.split(',')
chksum_y = Mychecksum(acknum + expectseqnum)
if chksum_r == chksum_y: # 通过校验和校验
base = int(acknum) + 1
if base == nextseqnum: # 滑窗第一个窗口接收到ack,计时停止
stop_timer = 1
print('结束计时')
else: # 代表接收端之前发送的的某个回应消息丢失了,但是由于是累计确认,仅需要开启新计时即可
stop_timer = 0
pickle.dump(0, open('flag', 'wb'))
timer1 = threading.Thread(target=mytimer)
timer1.start()
time.sleep(1)
时钟:
# 检查定时器
time.sleep(1)
flag = pickle.load(open('flag', 'rb'))
print('检查定时器flag:', flag)
if(flag == 1): # timeout 重新发送窗口中的所有包
print('重新发送')
nextseqnum = base
for i in range(window_size):
if base+i-1 < message_num:
message_flag[base+i-1] = 0
time.sleep(1)
print()
Receiver.py 关键代码:
while True:
f = random.random() # 产生(0,1)之间的随机数
# 模拟丢包
if(f < lost_probability):
rcvpkt = s.recv(1024).decode()
seqnum, message, chksum_r = rcvpkt.split(',')
if message == 'Message over': # 是否传输完毕
print(message)
print('messages:', *message_list)
print('Close') # 传输完毕,关闭连接
s.close()
break
print("Lost\n")
s.send('Lost'.encode()) # 实际上丢包时,接收端并不会产生任何回应,此处仅为模拟丢包
time.sleep(1)
# 未丢包
else:
rcvpkt = s.recv(1024).decode()
print('接收到的消息:', rcvpkt)
seqnum, message, chksum_r = rcvpkt.split(',')
chksum_y = Mychecksum(seqnum + message)
if chksum_r == chksum_y: # 校验和检验
if int(seqnum) == expectseqnum: # 检验收到的消息是否为期望的消息
if message == 'Message over': # 是否传输完毕
print(message)
print('\nmessages:', *message_list)
print('Close') # 传输完毕,关闭连接
s.close()
break
expectseqnum = expectseqnum + 1
ack = str(seqnum)
print(f'ACK{ack}:{message}') # 打印此次接收到的消息序号和消息内容
message_list.append(message) # 将此次消息放入消息组的列表中
chksum_s = Mychecksum(ack + str(expectseqnum)) # 计算校验和
sndpkt = ack + ',' + str(expectseqnum) + ',' + chksum_s # 发送响应消息:已收到的消息序号,期望的下一消息序号,校验和
print('发送消息:', sndpkt)
s.send(sndpkt.encode())
else: # 收到的消息不是期望的消息
s.send('Lost'.encode())
print('Not expected seq')
time.sleep(1)
print()
实验结果:
发送端:
Hi Sender_Alice. Welcome to Chat Room
Initialising....
DESKTOP-BK14LMU ( IP )
Waiting for incoming connections...
Received connection from IP ( 端口号 )
Receiver_Bob has connected to the chat room
发送消息: 1,123,0x9c9b # 发送第1条消息
开始计时
接收消息: 1,2,0xcecd # ack1
结束计时
检查定时器flag: 0
发送消息: 2,456,0x9895 # 发送第2条消息
开始计时
检查定时器flag: 0
发送消息: 3,654,0x9795 # 发送第3条消息
检查定时器flag: 0
发送消息: 4,788,0x9390 # 发送第4条消息
检查定时器flag: 1 # 在规定时间内,没有收到第2条消息的ack
重新发送
发送消息: 2,456,0x9895 # 重新发送的第2条消息
开始计时 # 重新计时
接收消息: 2,3,0xcdcc # ack2
结束计时
检查定时器flag: 0
发送消息: 3,654,0x9795 # 重新发送的第3条消息
开始计时
检查定时器flag: 0
发送消息: 4,788,0x9390 # 重新发送的第4条消息
检查定时器flag: 0
发送消息: 5,87364,0x5d60 # 发送第五条消息
检查定时器flag: 1 # 在规定时间内,没有收到第3条消息的ack
重新发送
发送消息: 3,654,0x9795 # 重新发送第3条消息
开始计时
接收消息: 3,4,0xcccb # ack3
结束计时
检查定时器flag: 0
发送消息: 4,788,0x9390 # 重新发送第4条消息
开始计时
接收消息: 4,5,0xcbca # ack4
结束计时
检查定时器flag: 0
发送消息: 5,87364,0x5d60 # 重新发送的第5条消息
开始计时
检查定时器flag: 0
发送消息: 6,875,0x9292 # 发送第6条消息
检查定时器flag: 0
发送消息: 7,8374,0x9249 # 发送第7条消息
检查定时器flag: 1 # 在规定时间内,没有收到第5条消息的ack
重新发送
发送消息: 5,87364,0x5d60 # 重新发送的第5条消息
开始计时
接收消息: 5,6,0xcac9 # ack5
结束计时
检查定时器flag: 0
发送消息: 6,875,0x9292 # 重新发送的第6条消息
开始计时
接收消息: 6,7,0xc9c8 # ack6
结束计时
检查定时器flag: 0
发送消息: 7,8374,0x9249 # 重新发送的第7条消息
开始计时
接收消息: 7,8,0xc8c7 # ack7
结束计时
检查定时器flag: 0
Message over # 消息已经全部发送成功,并接收方已全部接收
发送消息: 8,Message over,0xe97c
Close
接收端:
Hi Receiver_Bob. Welcome to Chat Room
Initialising....
Trying to connect to IP ( 端口号 )
Connected...
Sender_Alice has joined the chat room
接收到的消息: 1,123,0x9c9b
ACK1:123
发送消息: 1,2,0xcecd
Lost # 2号包丢失
接收到的消息: 3,654,0x9795
Not expected seq
接收到的消息: 4,788,0x9390
Not expected seq
接收到的消息: 2,456,0x9895 # 发送端重传了包2、3、4
ACK2:456
发送消息: 2,3,0xcdcc
Lost # 3号包丢失
接收到的消息: 4,788,0x9390
Not expected seq
Lost # 5号包丢失
接收到的消息: 3,654,0x9795 # 发送端重传了包3、4、5
ACK3:654
发送消息: 3,4,0xcccb
接收到的消息: 4,788,0x9390
ACK4:788
发送消息: 4,5,0xcbca
Lost # 5号包丢失
接收到的消息: 6,875,0x9292
Not expected seq
Lost # 7号包丢失
接收到的消息: 5,87364,0x5d60 # 发送端重传了包5、6、7
ACK5:87364
发送消息: 5,6,0xcac9
接收到的消息: 6,875,0x9292
ACK6:875
发送消息: 6,7,0xc9c8
接收到的消息: 7,8374,0x9249
ACK7:8374
发送消息: 7,8,0xc8c7
Message over
messages: 123 456 654 788 87364 875 8374
Close