需求:
使用SELECT或SELECTORS模块实现并发简单版FTP
允许多用户并发上传下载文件
流程图:
目录结构:
#server:
import os,time,socket,selectors
BASE_DIR=os.path.dirname(os.path.abspath(__file__))
class SelectFTPServer():
def __init__(self):
self.dic={}
self.HasReceived=0
self.sel=selectors.DefaultSelector()
self.create_socket()
self.handle()
def create_socket(self):
server=socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
server.setblocking(False)
self.sel.register(server,selectors.EVENT_READ,self.accept)
print('server已开启,等待client连接')
def handle(self):
while True:
events=self.sel.select()
for key,mask in events:
callback=key.data
callback(key.fileobj,mask)
def accept(self,sock,mask):
conn,addr=sock.accept()
print('从%s来的连接:%s'%(addr,conn))
conn.setblocking(False)
self.sel.register(conn,selectors.EVENT_READ,self.read)
self.dic[conn]={}
def read(self,conn,mask):
try:
if not self.dic[conn]:
data=conn.recv(1024)
cmd,filename,filesize=str(data,encoding='utf-8').split('|')
self.dic={conn:{'cmd':cmd,'filename':filename,'filesize':int(filesize)}}
if cmd=='put':
conn.send(bytes('ok',encoding='utf-8'))
if cmd=='get':
file=os.path.join(BASE_DIR,'download',filename)
if os.path.exist(file):
FileSize=os.path.getsize(file)
SendInfo='%s|%s'%('yes',FileSize)
conn.send(bytes(SendInfo,encoding='utf-8'))
else:
SendInfo='%s|%s'%('no',0)
conn.send(bytes(SendInfo,encoding='utf-8'))
else:
if self.dic[conn].get('cmd',None):
cmd=self.dic[conn].get('cmd')
if hasattr(self,cmd):
func=getattr(self,cmd)
func(conn)
else:
print('error cmd')
conn.close()
except Exception as e:
print('error:%s'%e)
self.sel.unregister(conn)
conn.close()
def put(self,conn):
FileName=self.dic[conn]['filename']
FileSize=self.dic[conn]['filesize']
path=os.path.join(BASE_DIR,'upload',FileName)
recv_data=conn.recv(1024)
self.HasReceived+=len(recv_data)
with open(path,'ab') as f:
f.write(recv_data)
if FileSize==HasReceived:
if conn in self.dic.key:
self.dic[conn]={}
print('%s上传完毕'%FileName)
def get(self,conn):
FileName=self.dic[conn]['filename']
path=os.path.join(BASE_DIR,'upload',FileName)
with open(file,'rb') as f:
seek(HasReceived)
content=f.read(1024)
recv_size=len(content)
self.HasReceived+=recv_size
self.sk.send(content)
if FileSize==HasReceived:
if conn in self.dic.key:
self.dic[conn]={}
print('%s下载完毕'%FileName)
if __name__=='__main__':
SelectFTPServer()
#client:
import socket,os,sys
BASE_DIR=os.path.dirname(os.path.abspath(__file__))
class SelectFTPClient():
def __init__(self):
self.args=sys.argv#命令行上输入的参数(文件路径,IP,端口)
if len(self.args)>1:
self.port=(self.args[1],int(self.args[2]))
else:
self.port=('127.0.0.1',8080)
create_socket()
command_fanout()
def create_socket(self):
try:
self.sk=socket.socket()
self.sk.connect(self.port)
print('成功连接FTP Server')
except Exception as e:
print('error:%s'%e)
def command_fanout(self):
while True:
cmd=input('请输入命令:').strip()
if cmd=='exit':
break
cmd,file=cmd.split('')
if hasattr(self,cmd):
func=getattr(self,cmd)
func(cmd,file)
else:
print('调用错误')
def put(self,cmd,file):
if os.path.isfile(file):
FileName=os.path.basename(file)
FileSize=os.path.getsize(file)
FileInfo='%s|%s|%s'%(cmd,FileName,FileSize)
self.sk.send(bytes(FileInfo,encoding='utf-8'))
RecvStatus=self.sk.recv(1024)
print('RecvStatus:%s'%RecvStatus)
HasSend=0
if str(RecvStatus,encoding='utf-8')=='ok':
with open(file,'rb') as f:
while HasSend<FileSize:
conten=f.read(1024)
recv_size=len(content)
self.sk.cend(content)
HasSend+=recv_size
s=str(int(HasSend/FileSize*100))+'%'
print('正在上传文件%s,已上传%s'%(FileName,s))
print('文件%s已上传完毕'%FileName)
else:
print('文件不存在')
def get(self,cmd,file):
FileName=file
FileSize=0
FileInfo='%s|%s|%s'%(cmd,FileName,FileSize)
self.sk.send(bytes(FileInfo,encoding='utf-8'))
Recv=self.sk.recv(1024)
print('Recv:%s'%Recv)
RecvStatus,FileSize=str(Recv,encoding='utf-8').split('|')
HasReceived=0
path=os.path.join(BASE_DIR,'download1',FileName)
if str(RecvStatus)=='yes':
with open(path,'ab') as f:
while HasRecieved<FileSize:
recv_data=conn.recv(1024)
f.write(recv_data)
HasReceived+=len(recv_data)
s=str(int(HasRecieved/FileSize*100))+'%'
print('正在下载文件%s,已下载%s'%(FileName,s))
print('文件%s已下载完毕'%FileName)
if __name__=='__main__':
SelectFTPClient()