作业要求:
- 用户加密认证
- 允许同时多用户登录
- 每个用户有自己的家目录 ,且只能访问自己的家目录
- 对用户进行磁盘配额,每个用户的可用空间不同
- 允许用户在ftp server上随意切换目录
- 允许用户查看当前目录下文件
- 允许上传和下载文件,保证文件一致性
- 文件传输过程中显示进度条
- 附加功能:支持文件的断点续传
未实现的功能:
磁盘配额、上传或下载时自动创建目录、断点续传
缺点:
1、没有对异常操作进行处理(比如 命令异常 文件不存在 路径不存在)
吐槽框:
脑子不够使,花了3天左右才做成这样子,惭愧,但是在不想做了,想接着往下学
改进思路:
1、磁盘配额:可以根据用户目录下文件的总大小
2、命令异常:对每一次的命令进行检测 不符合要求的 直接break或continue
3、文件夹不存在:自动创建
4、文件不存在:返回异常信息
程序结构:
main.py
'''
Created on 2018年5月28日
@author: hcl
'''
import os,sys
file_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
core_path = file_path + r'\core'
sys.path.append(file_path)
from core import ftp_server
from core import account_operation
if __name__ == "__main__":
server_start = ftp_server.Ftpserver_start()
server_start.start()
account_operation.py
'''
Created on 2018年5月28日
@author: hcl
'''
import os,pickle,hashlib,socket
from core import file_operation
file_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
db_path = file_path + r'\db'
class Save_account(object):
"""
storing the account message ,
and the password was encrypted by hashlib.md5
"""
def __init__(self,account,password):
m = hashlib.md5()
self.account = account
m.update(password.encode('utf-8'))
self.password = m.hexdigest()
def save_msg(self):
print(type(self.account))
filepath = db_path + '\\' + self.account
if not os.path.isdir(db_path):
os.mkdir(db_path)
print("make a new dir %s successful." %db_path)
else:
info = {}
info['account'] = self.account
info['password'] = self.password
with open(filepath,'wb') as f:
f.write(pickle.dumps(info))
f.flush()
print('----------save a new account successful----------')
class Check_account(object):
"""
check the account message ,
"""
def __init__(self):
self.account = ''
pass
def check_msg(self,account,password):
# account = input("Please input your account:").strip()
# password = input("Please input your password::").strip()
m = hashlib.md5()
filepath = db_path + '\\' + account
print(filepath)
if os.path.isfile(filepath):
with open(filepath,'rb') as f:
file_dict = pickle.load(f)
m.update(password.encode('utf-8'))
if file_dict['account'] == account and file_dict['password'] == m.hexdigest():
self.account = account
print("check correct")
return True
else:
print("invalid password")
return False
else:
print("invalid account")
return False
if __name__ == "__main__":
c1 = Check_account()
print(c1.check_msg('ln','ln'))
#
# s1 = Save_account("ln","ln")
# s1.save_msg()
# c1 = Check_account()
# tf = c1.check_msg()
# if tf:
# path = file_operation.Account_homepage(c1.account)
# print(path.ls())
# path.addpath('hecanlong')
# path.cd('../')
#
#
# else:
# print("wrong account")
file_operation.py
'''
Created on 2018年5月28日
@author: hcl
'''
import hashlib,os,socket
class Upfile(object):
"""
Upfile(filename)
"""
def __init__(self):
pass
def up(self,*args):
#print('up:',args )
print("***********Up Start***********")
if os.path.isfile(args[0]):
f = open(args[0],'rb')
m = hashlib.md5()
filesize = os.stat(args[0]).st_size
print('up filesize:',filesize)
if args[1] == 'client':
print('up from client')
ack = self.recv(1024)
self.send(str(filesize).encode(encoding='utf-8'))
else:
print('up from server')
ack = self.request.recv(1024)
self.request.sendall(str(filesize).encode(encoding='utf-8'))
#ack = self.recv(1024)
#self.send(str(filesize).encode(encoding='utf-8'))
for line in f:
m.update(line)#.encode(encoding='utf-8')
if args[1] == 'client':
self.send(line)#.encode(encoding='utf-8')
else:
self.request.sendall(line)
#self.send(line)#.encode(encoding='utf-8')
print('up file md5:',m.hexdigest())
f.close()
if args[1] == 'client':
self.send(m.hexdigest().encode(encoding='utf-8'))#
else:
self.request.sendall(m.hexdigest().encode(encoding='utf-8'))
#self.send(m.hexdigest().encode(encoding='utf-8'))#
print("***********Up Done***********")
class DownLoadfile(object):
"""
DownLoadfile(filepath)
"""
def __init__(self):
pass
def down(self,*args):
print("***********Down Start***********")
print('down:',args )
if args[1] == 'client':
self.send(b'ok')#
file_total_size = int(self.recv(1024).strip().decode())
else:
self.request.sendall(b'ok') # give a response to client
file_total_size = int(self.request.recv(1024).strip().decode())
#self.request.sendall(b'ok') # give a response to client
#file_total_size = int(self.request.recv(1024).strip().decode())
print('down file_total_size:',file_total_size)
recv_size = 0
filename = args[0]#[0][1]
print('filename:',filename)
f = open(filename + 'new','wb')
m = hashlib.md5()
pbar = Progressbar()
while recv_size != file_total_size:
if file_total_size - recv_size > 1024:
size = 1024
else:
size = file_total_size - recv_size
#print('last recv_size:',recv_size)
if args[1] == 'client':
data = self.recv(size)
else:
data = self.request.recv(size)
#data = self.request.recv(size)
recv_size += len(data)
m.update(data)
f.write(data)
f.flush()
#print('recv_size:',recv_size,'file_total_size:',file_total_size)
print("Processing:%.2f%%" %(pbar.progressing(recv_size, file_total_size)))
else:
new_file_md5 = m.hexdigest()
if args[1] == 'client':
server_datamd5 = self.recv(1024).decode()
else:
server_datamd5 = self.request.recv(1024).decode()
#server_datamd5 = self.request.recv(1024).decode()
print("file recv done",recv_size,file_total_size)
print("down_md5:",new_file_md5)
print('up_md5:',server_datamd5)
print("***********Down Done***********")
class Progressbar(object):
def __init__(self):
pass
def progressing(self,temp,total):
return temp/total*100
class Discretetrainsport(object):
"""
Discretetrainsport(filepath,continue_point)
"""
def __init__(self):
pass
def discrete(self,*args):
print(*args)
class Account_homepage(object):
def __init__(self,file):
self.file_path = file
def getrealpath(self):
rp = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + '\\home\\' + self.file_path
return rp
def addpath(self,file):
self.file_path = self.file_path + '\\' + file
return self.file_path
def decpath(self):
path_list = self.file_path.split('\\')
self.file_path = '\\'.join(path_list[0:-1])
return self.file_path
#def ls(self,send):
def ls(self):
print(os.listdir(self.getrealpath()))
#send.request.sendall(str(os.listdir(self.getrealpath())).encode(encoding='utf-8'))
return os.listdir(self.getrealpath())
def cd(self,*args):
base_path = self.getrealpath() + "\\"
path_len = len(self.file_path.split('\\'))
if not args[0] == "../":
if os.path.isdir(base_path+args[0]):
self.file_path = self.addpath(args[0])
print("new path:",self.file_path)
return self.file_path
else:
print(" ********* ",args[0]," doesn't exist.."," ********* ")
return (" ********* ",args[0]," doesn't exist.."," ********* ")
#return False
elif args[0] == "../" and path_len > 1:
self.file_path = self.decpath()
print("new path:",self.file_path)
return self.file_path
else:
print(" ********* You have got into the bottom ********* ")
return(" ********* You have got into the bottom ********* ")
#return False
def creat(self,*args):
pass
if __name__ == "__main__":
a1 = Account_homepage("hcl\home\ln")
a1.addpath('1234')
a1.decpath()
ftp_client.py
'''
Created on 2018年5月28日
@author: hcl
'''
import socket
from core import file_operation
from core import account_operation
client = socket.socket()
HOST,PORT = "localhost",50000
class Ftpclient_start(object):
def __init__(self):
pass
def start(self):
client.connect((HOST,PORT))
print(Clientcheck.check(client))
while True:
try:
msg = input(">>>:")
except:
break
else:
client.send(msg.encode()) # send cmd to the server
if msg.startswith('up'): # "up data.txt"
file_operation.Upfile.up(client,msg.split()[1],'client')
elif msg.startswith('download'):
file_operation.DownLoadfile.down(client,msg.split()[1],'client')
elif msg.startswith('ls'):
print('*****************ls start*****************')
rec_info = client.recv(1024).strip().decode()
print(rec_info)
print('*****************ls end*****************')
else:
print('*****************cd start*****************')
rec_info = client.recv(1024).strip().decode()
print(rec_info)
print('*****************cd end*****************')
class Clientcheck(object):
def __init__(self):
pass
def check(self):
account = input("Please input your account:").strip()
password = input("Please input your password:").strip()
#info = [account,password]
info = account + ' ' +password
self.send(str(info).encode('utf-8'))
rec_info = self.recv(1024).strip().decode()
print(rec_info)
return rec_info
if __name__ == "__main__":
client_start = Ftpclient_start()
client_start.start()
ftp_server.py
'''
Created on 2018年5月28日
@author: hcl
'''
import socketserver
from core import file_operation
from core import account_operation
HOST,PORT = "localhost",50000
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
op = {
"cd":file_operation.Account_homepage.cd,
"ls":file_operation.Account_homepage.ls,
"up":file_operation.DownLoadfile.down,
"download":file_operation.Upfile.up,
"check":account_operation.Check_account.check_msg
}
account_name = Servercheck.check(self)
login_account = file_operation.Account_homepage(account_name)
print("login account:",account_name)
while True:
try:
self.data = self.request.recv(1024).strip() # analysize the cmd data from client
if not self.data:
continue
cmd_data = self.data.decode()
cmd_dict = cmd_data.split()
#if len(cmd_dict) > 1:
if cmd_dict[0] == 'up' or cmd_dict[0] == 'download':
op[cmd_dict[0]](self,cmd_dict[1],'server')
elif cmd_dict[0] == 'cd':
#op[cmd_dict[0]](login_account,cmd_dict[1])
self.request.sendall(str(op[cmd_dict[0]](login_account,cmd_dict[1])).encode(encoding='utf-8'))
else: #ls
#op[cmd_dict[0]](login_account,self)
self.request.sendall(str(op[cmd_dict[0]](login_account)).encode(encoding='utf-8'))
except ConnectionResetError as e:
print("MyTCPHandler handle err:", e)
break
class Ftpserver_start(object):
def __init__(self):
pass
def start(self):
print('socketserver start.....')
server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)
server.serve_forever()
class Servercheck(object):
def __init__(self):
pass
def check(self):
info = []
info = self.request.recv(1024).strip().decode().split()
print("server recv info:",info[0],info[1])
c1 = account_operation.Check_account()
acc = c1.check_msg(info[0],info[1])
self.request.sendall(str(acc).encode(encoding='utf-8'))
return info[0]
程序运行 启动main.py ftp_client.py
客户端1操作:
Please input your account:hcl
Please input your password:hcl
True
True
>>>:ls
*****************ls start*****************
['123', '213.txt', 'asdjk.txt']
*****************ls end*****************
>>>:cd 123
*****************cd start*****************
hcl\123
*****************cd end*****************
>>>:ls
*****************ls start*****************
[]
*****************ls end*****************
>>>:cd ../
*****************cd start*****************
hcl
*****************cd end*****************
>>>:ls
*****************ls start*****************
['123', '213.txt', 'asdjk.txt']
*****************ls end*****************
>>>:up data.txt
***********Up Start***********
up filesize: 938
up from client
up file md5: e7f3675df6fa4058b86bb9d7d8004eb5
***********Up Done***********
>>>:download 123.txt
***********Down Start***********
down: ('123.txt', 'client')
down file_total_size: 938
filename: 123.txt
Processing:73.56%
Processing:89.87%
Processing:95.95%
Processing:100.00%
file recv done 938 938
down_md5: e7f3675df6fa4058b86bb9d7d8004eb5
up_md5: e7f3675df6fa4058b86bb9d7d8004eb5
***********Down Done***********
>>>:
客户端2操作:
Please input your account:ln
Please input your password:ln
True
True
>>>:up data.txt
***********Up Start***********
up filesize: 938
up from client
up file md5: e7f3675df6fa4058b86bb9d7d8004eb5
***********Up Done***********
>>>:download 123.txt
***********Down Start***********
down: ('123.txt', 'client')
down file_total_size: 938
filename: 123.txt
Processing:68.02%
Processing:82.62%
Processing:86.89%
Processing:87.10%
Processing:89.87%
Processing:90.09%
Processing:95.95%
Processing:100.00%
file recv done 938 938
down_md5: e7f3675df6fa4058b86bb9d7d8004eb5
up_md5: e7f3675df6fa4058b86bb9d7d8004eb5
***********Down Done***********
>>>:
服务器输出:
socketserver start.....
server recv info: hcl hcl
D:\F\eclipse-workspace\ftp\src\db\hcl
check correct
login account: hcl
['123', '213.txt', 'asdjk.txt']
new path: hcl\123
[]
new path: hcl
['123', '213.txt', 'asdjk.txt']
***********Down Start***********
down: ('data.txt', 'server')
down file_total_size: 938
filename: data.txt
Processing:60.77%
Processing:86.89%
Processing:87.10%
Processing:90.09%
Processing:95.95%
Processing:100.00%
file recv done 938 938
down_md5: e7f3675df6fa4058b86bb9d7d8004eb5
up_md5: e7f3675df6fa4058b86bb9d7d8004eb5
***********Down Done***********
***********Up Start***********
up filesize: 938
up from server
up file md5: e7f3675df6fa4058b86bb9d7d8004eb5
***********Up Done***********
server recv info: ln ln
D:\F\eclipse-workspace\ftp\src\db\ln
check correct
login account: ln
***********Down Start***********
down: ('data.txt', 'server')
down file_total_size: 938
filename: data.txt
Processing:68.02%
Processing:82.62%
Processing:87.10%
Processing:89.87%
Processing:90.09%
Processing:95.95%
Processing:100.00%
file recv done 938 938
down_md5: e7f3675df6fa4058b86bb9d7d8004eb5
up_md5: e7f3675df6fa4058b86bb9d7d8004eb5
***********Down Done***********
***********Up Start***********
up filesize: 938
up from server
up file md5: e7f3675df6fa4058b86bb9d7d8004eb5
***********Up Done***********
六、有兴趣接电子设计相关小型项目的请加下群,每个项目一般在1000元以内,非诚勿扰