You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
teamwk123/users/xuchen/server.py

125 lines
3.4 KiB

1 month ago
#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()