#coding=utf8 ''' 服务器端 udp协议 方法: 1.通过多线程实现停等协议来进行可靠传输, 2.通过压缩、分块实现文件的快速重传 ''' import socket import os import hashlib from os.path import basename from threading import Thread,Event from time import sleep import gzip #-------------------------------配置-------------------------------------------- #------------------------------config-------------------------------------------- ip='192.168.230.143' #client的ip port=7777 #client的port BUFFER_SIZE= 32*1024 #32Kb进行分块处理 fn_path='output.bin' #原始文件名 fn_path_zip=fn_path+".gz"#压缩文件的文件名称 port_ack=7778 address=(ip,port) #------------------------------中间文件------------------------------------------- positions=[] #存放每块数据在文件中的起点 file_name=[f'{basename(fn_path)}'.encode("utf-8")] #------------------------------大文件压缩------------------------------------------- BufSize = 1024*16 def gZipFile(src, dst): fin = open(src, 'rb') fout = gzip.open(dst, 'wb') in2out(fin, fout) def gunZipFile(gzFile, dst): fin = gzip.open(gzFile, 'rb') fout = open(dst, 'wb') in2out(fin, fout) def in2out(fin, fout): while True: buf = fin.read(BufSize) if len(buf) < 1: break fout.write(buf) fin.close() fout.close() #关闭文件 #------------------------------发送函数------------------------------------------- def sendto(fn_path): #用来发送文件的线程函数 #首先对文件进行压缩 #gZipFile(fn_path,fn_path_zip) #读取压缩文件全部内容 with open(fn_path,'rb') as fp: content=fp.read() #获取文件大小,做好分块传输 fn_size=len(content) for start in range(fn_size//BUFFER_SIZE+1): positions.append(start*BUFFER_SIZE) #设置事件,启动用来接收确认信息的线程 e.set() #创建窗口套接字,设置发送缓冲区大下 sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET,socket.SO_SNDBUF,BUFFER_SIZE) #发送文件数据,直到所有分块都收到确认,否则就不停地发送 while positions: for pos in positions: sock.sendto(f'{pos}_'.encode()+content[pos:pos+BUFFER_SIZE],address) print(len(positions)) #通知其发送成功 while file_name: sock.sendto(b'over_'+file_name[0],address) #关闭套接字 sock.close() def recv_ack(): #用来接收确认信息的线程函数 #创建套接字,绑定本地端口,用来接收对方的确认信息 sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) sock.bind(('',port_ack)) #如果所有分块都确认过,结束循环 while positions: #预期收到的确认包格式为1234_ack data,_=sock.recvfrom(1024) pos =int(data.split(b'_')[0]) if pos in positions: positions.remove(pos) #确认对方收到文件名,并已接收全部数据 while file_name: data,_=sock.recvfrom(1024) fn=data.split(b'_')[0] if fn in file_name: file_name.remove(fn) #关闭套接字 sock.close() t1=Thread(target=sendto,args=(fn_path,)) t1.start() e=Event() e.clear() e.wait() t2=Thread(target=recv_ack) t2.start() #等待发送线程和接收确认线程都结束 t2.join() t1.join()