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: wenjinLi
import selectors
import socket
import os,sys
import time
import errno
BASE_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: wenjinLi
import socket
import time
import os,sys
import errno
BASE_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()