实验内容:采用UDP Socket编程接口作为模拟物理层接口实现帧的发送和接收,协议采用双工方式进行数据通信。假设Host1和Host2分别向对方发送大文件,Host1先发送一帧到Host2,通过数据链路层的帧每次完成数据块的可靠传输,采用GBN协议,差错编码采用CRC-CCITT标准。
程序结构:分别调用Server.py开启进程1(Host1)和进程2(Host2),Host1和Host2可选择接收,发送或者关闭。接收则会调用Receiver.py创建接收线程,发送则会调用sender.py创建发送线程。Receiver和sender都通过UDT.py实现帧的发送。Timer.py实现计时器,实现超时重传。Packet.py实现组帧和提取帧中数据的功能。PDU.py定义帧格式。crc16.py是引入的库,实现计算crc和校验功能。
#sender.py
import socket
import PDU
import UDT
import _thread
import timer
import sys
import packet
import crc16
import time
import threading
interval=1
ack_expected=0 #累计确认,只用维护一个
num_packets=0
send_timer=timer.timer(interval)
log_filename=""
mutex = _thread.allocate_lock()
UDTER=UDT.UDT(0.0001,0.0001)
def send(sock,filename,IP_PORT,RECEIVER_ADDR):
global UDTER
global mutex
global ack_expected
global num_packets
global send_timer
global log_filename
#Create log file
log_filename=IP_PORT[0]+"_"+str(IP_PORT[1])+"_"+"log_file.txt"
log_file=open(log_filename,"a+")
file=open(filename,"rb")
log_file.write("-------------------------------\n")
log_file.write("%s send %s to %s\n" % (IP_PORT[0]+" "+str(IP_PORT[1]),filename,RECEIVER_ADDR[0]+" "+str(RECEIVER_ADDR[1])))
packets=[]
seq_num=0
while True:
data=file.read(512) #data size
if not data:
break
crc_num=crc16.crc16xmodem(data) #calculate crc
pdu=packet.make(seq_num,crc_num,data) #make packet
packets.append(pdu)
seq_num+=1
num_packets = len(packets)
log_file.write("total %d packets(512bytes)\n" %(num_packets))
print('I gots', num_packets)
#set window size here
window_size=200
next_frame_to_send=0
#start receive ack thread
THREAD=threading.Thread(target=receive,args=(sock,))
THREAD.start()
overtime_flag=0
scale=50 #using to draw progress bar
start = time.perf_counter()
pre=start
while ack_expected<len(packets):
mutex.acquire() #send thread acquire lock
while next_frame_to_send<ack_expected+window_size:
if next_frame_to_send>=len(packets):
break
#print('Sending packet', next_frame_to_send)
if overtime_flag==0:
log_file.write("%s: Send PDU=%d,STATUS=New,ACKed=%d to %s\n" % (time.ctime(),next_frame_to_send,ack_expected,str(RECEIVER_ADDR)))
elif overtime_flag==1:
log_file.write("%s: Send PDU=%d,STATUS=TO,ACKed=%d to %s\n" % (time.ctime(),next_frame_to_send,ack_expected,str(RECEIVER_ADDR)))
send_timer.satrt(next_frame_to_send)
UDTER.send(packets[next_frame_to_send],sock,RECEIVER_ADDR)
next_frame_to_send+=1
overtime_flag=0
if send_timer.overtime(ack_expected):
#print("overtime")
overtime_flag=1
next_frame_to_send=ack_expected
if (time.perf_counter()-pre) > 1:
pre=time.perf_counter()
param = (int) (num_packets/50)
i = (int) (next_frame_to_send/param)
a='*' *i
b='.'*(scale-i)
c=(i/scale)*100
dur=pre-start
print("\r{:^3.0f}%[{}->{}]{:.2f}s".format(c,a,b,dur),end='')
mutex.release()
print("\nover")
UDTER.send(packet.make_empty(), sock, RECEIVER_ADDR)
log_file.write("send succeed\n")
log_file.write("-------------------------------\n\n\n")
file.close()
log_file.close()
def receive(sock):
global mutex
global ack_expected
global num_packets
while True:
ack,_=UDTER.recvack(sock)
#print('Got Ack',ack)
if ack>=ack_expected:
mutex.acquire()
ack_expected=ack+1
# print('ack_expected',ack_expected)
mutex.release()
if ack_expected>=num_packets:
break
# receiver.py
import socket
import packet
import crc16
import UDT
import sys
import time
def receive(sock,filename,IP_PORT):
UDTER=UDT.UDT(0.0001,0.0001)
file=open(filename,"wb")
log_filename=IP_PORT[0]+"_"+str(IP_PORT[1])+"_"+"log_file.txt"
log_file=open(log_filename,"a+")
log_file.write("-------------------------------\n")
frame_expected=0
log_file.write("Receiving %s...\n" %(filename))
while True:
pdu,addr=UDTER.recv(sock)
#print(pdu)
if not pdu:
break
seq_num,crc_num,data=packet.extract(pdu)
#print('Got PDU',seq_num)
crc_expected=crc16.crc16xmodem(data)
if crc_expected!=crc_num:
log_file.write("%s: Receive PDU=%d,STATUS=DataErr,FRAME_EXPECTED=%d from %s\n" %(time.ctime(),seq_num,frame_expected,str(addr)))
#print("data with error")
continue
if seq_num==frame_expected:
#print('Got expected packet')
log_file.write("%s: Receive PDU=%d,STATUS=OK,FRAME_EXPECTED=%d from %s\n" %(time.ctime(),seq_num,frame_expected,str(addr)))
#print('Sending ACK', frame_expected)
UDTER.sendack(frame_expected,sock,addr)
frame_expected+=1
file.write(data)
else:
#print('Got unexpected packet')
log_file.write("%s: Receive PDU=%d,STATUS=NoErr,FRAME_EXPECTED=%d from %s\n" %(time.ctime(),seq_num,frame_expected,str(addr)))
#print('Sending ACK', frame_expected-1)
UDTER.sendack(frame_expected-1,sock,addr)
print("over")
log_file.write("Receive succeed\n")
log_file.write("-------------------------------\n\n\n")
log_file.close()
file.close()
?
视频演示:
GBN-Python演示_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1eL411A7qo/
完整代码:GBN-Python: 用python实现GBN(后退N帧协议),进行文件传输验证。 (gitee.com)https://gitee.com/chris-william/gbn-python?
|