使用selectors实现了ftp并发,实现多个客户端同时链接,同时上传下载文件,但是不能同时上传下载同一个文件。
只是简单的实现了功能,仅供参考。
目录
服务端
import selectors,socket,struct,json,os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class SelSocket:
def __init__(self):
self.sel = selectors.DefaultSelector() #根据系统
self.fileinfo = {} #主要是将文件的信息与conn联系起来
self.has_recv = {} #新文件上传时置空
self.create_ser()
self.handle()
def create_ser(self):
ser = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ser.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
ser.bind(('192.2.12.00', 8090)) #改为自己的ip
ser.listen(5)
ser.setblocking(False) # socket对象设置为非阻塞
self.sel.register(ser, selectors.EVENT_READ, self.accept) #注册
print('servering..........')
def handle(self): #就这么写
while True:
events = self.sel.select() #检测
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)
def accept(self,ser,mask):
conn,addr = ser.accept()
print('receive connect %s from %s' %(conn,addr))
conn.setblocking(False) #链接也设为非阻塞
self.sel.register(conn,selectors.EVENT_READ,self.read)
self.fileinfo[conn]={}
def read(self,conn,mask): #主要逻辑
try:
if not self.fileinfo[conn]:
msg = conn.recv(1024).decode('utf-8')
self.fileinfo.update({conn:json.loads(msg)})
#fileinfo = {conn:{'cmd':put,'filename':'filename','filesize':filesize}}
if self.fileinfo[conn]['cmd'] == 'put':
conn.sendall('True'.encode('utf-8'))
elif self.fileinfo[conn]['cmd'] == 'download':
self.file = os.path.join(BASE_DIR,'download',self.fileinfo[conn]['filename'])
if os.path.exists(self.file):
filesize = int(os.path.getsize(self.file))
fileinfo = json.dumps({'stat':'True','filesize':filesize}).encode('utf-8')
conn.sendall(fileinfo)
else:
fileinfo = json.dumps({'stat':'False','filesize':0}).encode('utf-8')
conn.sendall(fileinfo)
self.fileinfo[conn] = {}
else:
cmd = self.fileinfo[conn]['cmd']
if hasattr(self,cmd):
func = getattr(self,cmd)
func(conn)
else:
conn.sendall('cmd error!'.encode('utf-8'))
conn.close()
except Exception as e: #当发生错误时异常断开链接时
print(e)
self.sel.unregister(conn)
conn.close()
if self.has_recv.get(conn):
del self.has_recv[conn]
def put(self,conn):
filename = self.fileinfo[conn]['filename']
filesize = self.fileinfo[conn]['filesize']
path = os.path.join(BASE_DIR,'upload',filename)
data = conn.recv(1024)
print(data)
if not data:
raise Exception
if not self.has_recv.get(conn):
self.has_recv[conn] = 0
self.has_recv[conn] += len(data)
with open(path,'ab') as f:
f.write(data)
if filesize == self.has_recv[conn]:
self.fileinfo[conn] = {}
del self.has_recv[conn]
print('%s 上传完成!'%filename)
def download(self,conn):
back_msg = conn.recv(1024).decode('utf-8')
if back_msg == 'OK':
with open(self.file,'rb') as f:
for line in f:
conn.sendall(line)
self.fileinfo[conn] = {}
print('传输完成')
if __name__ == '__main__':
SelSocket()
客户端
import socket
import os,sys,json
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class SelClient:
def __init__(self):
self.create_cli()
self.run()
def create_cli(self):
self.cli = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.cli.connect(('192.2.12.00', 8090))
print('链接成功!')
def run(self):
while True:
try:
msg = input('>>').strip()
if not msg:continue
cmd = msg.split()[0]
filename = msg.split()[1]
# filesize = os.path.abspath(filename).split('/')[-1]
# fileinfo = {'cmd':cmd,'filename':filename,'filesize':filesize}
if hasattr(self,cmd):
func = getattr(self,cmd)
func(cmd,filename)
except Exception as e:
print(e)
def put(self,cmd,filename):
if os.path.exists(filename):
file_name = os.path.basename(filename)
file_size = int(os.path.getsize(filename))
file_info = json.dumps({'cmd':cmd,'filename':file_name,'filesize':file_size})
self.cli.sendall(file_info.encode('utf-8'))
backmsg = self.cli.recv(1024).decode('utf-8')
if backmsg == 'True':
has_send = 0
with open(filename,'rb') as f:
while has_send < file_size:
data = f.read(1024)
self.cli.sendall(data)
has_send += len(data)
percent = str(int(has_send*100/file_size))+'%'
print(file_name + " 已经上传:" + percent)
else:
print('文件不存在!')
def download(self,cmd,filename):
file_path = os.path.join(BASE_DIR,'download',filename)
file_info = json.dumps({'cmd':cmd,'filename':filename})
self.cli.sendall(file_info.encode('utf-8'))
stat = json.loads(self.cli.recv(1024).decode('utf-8'))
if stat['stat'] == 'True':
self.cli.sendall('OK'.encode('utf-8'))
has_download = 0
with open(file_path,'ab') as f:
while has_download < stat['filesize']:
data = self.cli.recv(8192)
f.write(data)
has_download += len(data)
percent = str(int(has_download*100/stat['filesize']))+'%'
print(filename+' 已经下载:'+percent)
print('文件下载完毕!!!')
else:
print('文件不存在!!!')
if __name__ == '__main__':
SelClient()