1.文件目录
selector_FTP ——————
clientfile —————— 存放用户文件路径
selector_client.py——
selector_server.py——
serverfile —————— 存放服务器文件路径
注:实现的简约版的FTP,不支持断点续传、断点下载等操作
且只支持普通上传下载 用selector实现,支持多条客户端链接同时下载、上传 ,切勿多个客户端同时上传、下载同一个文件。
服务端
selector_server.py
def get(conn,cmd): 下载文件
def put(conn,cmd): 上传文件
def accept(sock, mask): selector 注册方法
def read(conn, mask): selector 注册方法
selector_server.py
def get(conn,cmd): 下载文件
def put(conn,cmd): 上传文件
def progress(recive_size,file_size,tips) 显示下载进度方法
例子:
clientfile 里面有一个3D.pdf 在客户端输入命令 put 3D.pdf 后 serverfile 文件夹下会有3D.pdf
serverfile 里面有一个test.zip 在客户端输入命令 get test.zip 后 clientfile 文件夹下会有test.zip
服务器端:
#!/usr/bin/env python# -*- coding: utf-8 -*-# Author: wenjinLiimport selectorsimport socketimport os,sysimport timeimport errnoBASE_DIR = os.path.dirname(os.path.abspath(__file__))sel = selectors.DefaultSelector()def get(conn,cmd): if len(cmd.split()) > 1: filename = cmd.split()[1] file_path = '%s/serverfile/%s' % (BASE_DIR, filename) #文件路径 print(file_path) if os.path.isfile(file_path): #判断文件是否存在 client_file_size = 0 file_size = os.stat(file_path).st_size #获得文件大小 conn.send(str(file_size).encode()) #将文件大小传给客户端 time.sleep(0.3) while True: with open(file_path, 'rb') as f: try: for line in f: client_file_size += len(line) #记录已经传送的文件大小 conn.sendall(line) print(client_file_size, file_size) except IOError as e: if e.errno != errno.EAGAIN: # errno.EAGAIN 缓冲区满 等待下 raise else: time.sleep(0.1) #等待0.1s进行下一次读取 if client_file_size >= int(file_size): #文件传送完毕 print('break') break else: conn.sendall('405'.encode()) # 文件不存在 else: conn.sendall('401'.encode()) # 命令不存在,无法操作def put(conn,cmd): filename = cmd.split()[1] filename = "%s/serverfile/%s" % (BASE_DIR, filename) #文件路径 with open(filename, 'ab') as f: data = conn.recv(1024).decode() total_size = int(data) if total_size == '405' or total_size == '401': #文件不存在 或命令有误 return None current_size = 0 while current_size <= total_size: try: recivesize = total_size - current_size if recivesize > 1024: recivesize = 1024 data = conn.recv(recivesize) if not data: #客户端断开 print('closing', conn) sel.unregister(conn) conn.close() break current_size += len(data) f.write(data) except IOError as e: if e.errno != errno.EAGAIN: raise else: time.sleep(0.1) print('接收完毕')def accept(sock, mask): conn, addr = sock.accept() # Should be ready conn.setblocking(False) sel.register(conn, selectors.EVENT_READ, read)def read(conn, mask): data = conn.recv(1000) # Should be ready if data: print(data) command_str = data.decode() print(command_str) if command_str.split()[0] == 'put': put(conn, command_str) elif command_str.split()[0] == 'get': get(conn,command_str) else: conn.send(b'404') # Hope it won't block else: print('closing', conn) sel.unregister(conn) conn.close()sock = socket.socket()sock.bind(('localhost', 1112))sock.listen(100)sock.setblocking(False)sel.register(sock, selectors.EVENT_READ, accept)while True: events = sel.select() for key, mask in events: callback = key.data callback(key.fileobj, mask)
用户端:
#!/usr/bin/env python# -*- coding: utf-8 -*-# Author: wenjinLiimport socketimport timeimport os,sysimport errnoBASE_DIR = os.path.dirname(os.path.abspath(__file__))def get(s,command): '''下载''' if len(command.split()) == 2: s.send(command.encode()) #通知服务器 要上传文件了 else: print('下载命令有误') filename = command.split()[1] filename = "%s/clientfile/%s" % (BASE_DIR, filename) #本地地址 with open(filename, 'ab') as f: data = s.recv(1024) print(data) data = data.decode() total_size = int(data) if total_size == '405': print('文件不存在') return None current_size = 0 while current_size <= total_size: try: recivesize = total_size - current_size if recivesize > 1024: recivesize = 1024 data = s.recv(recivesize) if not data: break current_size += len(data) progress(current_size, total_size, '下载中...') #打印进度 print(current_size, total_size) f.write(data) except IOError as e: if e.errno != errno.EAGAIN: raise else: time.sleep(0.1) #若 系统缓存已满则等带0.1秒后开始 print('接收完毕')def put(s,command): '''上传''' if len(command.split()) == 2: s.send(command.encode()) #告诉服务器要上传文件 else: print('上传命令有误') if len(command.split()) > 1: filename = command.split()[1] file_path = '%s/clientfile/%s' % (BASE_DIR, filename) print(file_path) if os.path.isfile(file_path): current_file_size = 0 file_size = os.stat(file_path).st_size s.send(str(file_size).encode()) time.sleep(0.3) with open(file_path, 'rb') as f: try: for line in f: s.sendall(line) current_file_size += len(line) progress(current_file_size, file_size, '上传中...') print(current_file_size,file_size) except IOError as e: if e.errno != errno.EAGAIN: # errno.EAGAIN 缓冲区满 等待下 raise else: time.sleep(0.1) # 等待0.1s进行下一次读取 else: s.send('405'.encode()) print('文件不存在') else: s.send('401'.encode()) print('命令不存在')def progress(recive_size,file_size,tips): '''***代表加载 _____表示还未加载部分''' lensize = 100 percent = float(recive_size) / float(file_size) recivenumber = '*' * int(percent * lensize) # 已经加载的所占格数 leftnumber = '_' * (lensize - int(percent * lensize)) # 未加载的所占格数 sys.stdout.write('%s:%d%% [%s]\n' % (tips, percent * lensize, recivenumber + leftnumber)) sys.stdout.flush()if __name__ == '__main__': HOST = 'localhost' PORT = 1112 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect((HOST,PORT)) while True: msg = input('>>>请输入操作命令:').strip() if len(msg) == 0: continue command = msg.split()[0] if command == 'get': '''下载''' get(s,msg) elif command == 'put': '''上传''' put(s, msg) elif command == 'exit': print('成功退出') break else: continue s.close()