importsocketimportosimportjsonimporthashlib
user= "none" #当用户成功登录后,就会改变这个全局变量为当前用户名
classFtpClient(object):def __init__(self):
self.client=socket.socket()defconnection(self,ip,port):"""客户端去连接服务端
:param ip: 服务端IP地址
:param port: 服务端端口号"""self.client.connect((ip,port))deflogin(self):"""用户登录验证
:return:登录成功返回True,登录失败返回False"""m= hashlib.md5() #设定加密类型
username = input("[login]:").strip()
passw= input("[login]:").strip()
m.update(bytes(passw,encoding="utf-8")) #对密码进行加密
password = m.hexdigest() #返回十六进制的加密后的密码,加密时必须对bytes类型数据加密,使用bytes时必须指定encoding
user_dic ={"username":username,"password":password, #这里的密码已经是加密了的
"action":"login"}
user_dic_byte= str(json.dumps(user_dic)).encode() #将这个字典转化为bytes类型,传给服务端
self.client.send(user_dic_byte)
login_res_byte= self.client.recv(1024) #返回用户验证后的状态码(用户名或者密码对不对)
login_res =login_res_byte.decode()if login_res == "101": #用户名和密码正确
globaluser
user=usernameprint("Login successed")returnTrueelif login_res == "102": #用户存在,密码错误
print("password error")returnFalseelif login_res == "103": #用户不存在
print("user {} does not exist".format(username))returnFalsedefregist(self):"""用户注册"""m=hashlib.md5()
username= input("[regist]:").strip()
passw= input("[regist]:").strip()
m.update(bytes(passw,encoding="utf-8")) #加密时必须对bytes类型数据加密,使用bytes时必须指定encoding
password = m.hexdigest() #返回十六进制的加密后的密码
user_dic ={"username": username,"password": password, #这里的密码已经是加密了的
"action": "regist"}
user_dic_byte=str(json.dumps(user_dic)).encode()
self.client.send(user_dic_byte)
regist_res_byte= self.client.recv(1024)
regist_res=regist_res_byte.decode()if regist_res == "104": #用户注册成功
print("Regist successed")returnFalseelif regist_res == "105": #用户已经存在
print("user {} exist".format(username))returnFalsedefuserlogin(self):"""实现用户登录或注册的交互"""
whileTrue:
option= input("(L/l or G/g):").strip()if option.upper() == "L" andself.login():break
elif option.upper() == "G":
self.regist()else:continueself.interact()definteract(self):"""实行客户端与服务端的交互"""
whileTrue:
opt= input("[{}@ftpserver ~]#".format(user)).strip()if len(opt) == 0: continuecmd= opt.split()[0] #取出命令
if hasattr(self,cmd): #通过反射,执行相应的函数(一个命令对应一个函数,服务端也是如此)
func =getattr(self,cmd)
func(opt)else:
self.help()defhelp(self):"""命令使用帮助信息"""msg= """命令使用帮助:
ls 查看家目录下的文件目录
put file 上传文件
get file 下载文件
exit 退出当前用户"""
print(msg)defprogress(self,passed,all):"""进度条显示
:param passed: 已经进行过的长度
:param all: 进度条总长度"""s= passed * 100 //all
a= s * "#"b= (100 - s) * "-"
print('\r %d%% [%s%s]' % (s, a, b), end="") #光标回到行首,两个百分号表示输出一个百分号,end默认为\n,意为换行,改为空格就不换行
def put(self,*args):"""客户端上传文件"""cmd_list= args[0].split() #将命令和文件名分开
if len(cmd_list) > 1: #判断输入的命令是否正确,列表里命令加上文件名至少是两个元素
filename = cmd_list[1]if os.path.isfile(filename): #判断文件是否存在
filesize = os.stat(filename).st_size #获取文件的大小
file_dic ={"filename":filename,"filesize":filesize,"action":"put", #action是为了告诉服务端该执行什么操作
"fileuser":user #传这个值是因为要告诉服务端把文件放到哪个用户的家目录
}
self.client.send(json.dumps(file_dic).encode())
responce= self.client.recv(1024) #防止粘包,后续可用作验证返回
f = open(filename,"rb")
recvd_size=0for line inf:
recvd_size+=len(line)
self.progress(recvd_size,filesize)#进度条函数,传入已传文件大小和总文件大小
self.client.send(line)else:print("\nfile upload finish")
f.close()else:print("'" + filename + "': No such file")def get(self,*args):"""客户端下载文件"""cmd_list=args[0].split()if len(cmd_list) > 1:
filename= cmd_list[1]
file_dic={"filename":filename,"action":"get","fileuser": user
}
self.client.send(json.dumps(file_dic).encode())
file_size_byte= self.client.recv(1024) #传回的要么是文件大小,要么是状态码,下面就做判断
if file_size_byte.decode() != "404 none":
file_size=int(file_size_byte.decode())
self.client.send(b"100")
recvd_size=0ifos.path.isfile(filename):
f= open(filename + ".new","wb")else:
f= open(filename,"wb")while recvd_size < file_size: #判断已接收的文件大小和文件总大小
data = self.client.recv(1024)
f.write(data)
recvd_size+=len(data)
self.progress(recvd_size,file_size)#进度条
else:
f.close()print("\nfile has download")else:print("file no exist")def exit(self,*args):#退出当前用户
self.userlogin()def ls(self,*args):"""查看用户家目录下的所有文件"""ls_dic={"username":user,"action":"ls",
}
self.client.send(json.dumps(ls_dic).encode())
data= self.client.recv(1024)if data.decode() != "130 none": #要么返回列表,要么返回状态码
ls_list =json.loads(data.decode())for i inls_list:print(i)
ftp=FtpClient()
ftp.connection("localhost",9998)
ftp.userlogin()