importoptparseimportsocketimportjsonimportreimportosimportsys
STATUS_CODE={250:"Invalid cmd format,e.g:{'action':'get','filename':'test.py','size':344}",251:"Invalid cmd",252:"Invalid auth data",253:"Wrong username or password",254:"Passed authentication",255:"Filename doesn't provided",256:"File doesn't exist on server",257:"ready to send file",258:"md5 verification",800:"the file exist,but not enough,is continue",801:"the file exist",802:"ready to receive datas",900:"md5 valdate success",
}classClientHandler():def __init__(self):
self.op=optparse.OptionParser()
self.op.add_option('-S','--server',dest='server')
self.op.add_option('-P', '--port', dest='port')
self.op.add_option('-u', '--username', dest='username')
self.op.add_option('-p', '--password', dest='password')
self.options, self.args=self.op.parse_args()#上传文件的绝对路径
#os.path.abspath(__file__):获得当前文件的绝对路径
#os.path.dirname(os.path.abspath(__file__)):获得上一级目录
self.mainPath=os.path.dirname(os.path.abspath(__file__))
self.last=0#下面两个函数是实例化类的时候就执行
#对参数进行处理
self.verify_args(self.options)#连接服务端,进行用户操作
self.make_connection()#对参数进行处理
defverify_args(self,options):
server=options.server
port=options.port
username=options.username
password=options.password#对参数进行简单判断
if re.match(r"^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",str(server)):returnTrueelse:
exit("IP vaild")if int(port) and int(port) < 65535:returnTrueelse:
exit("the port is in 0-65535")if username isNone:returnTrueelse:
exit("用户名为空")if password isNone:returnTrueelse:
exit("密码为空")#连接服务端
defmake_connection(self):#创建socket
self.sock=socket.socket()
self.sock.connect((self.options.server,int(self.options.port)))#用户操作
definteractive(self):#先进行身份认证
ifself.authenticate():print('''请选择你要进行的操作:
1.上传文件示例:put filename file
2.查看文件:ls
3.进入目录:cd dirname
4.创建目录:mkdir dirname
5.退出:quit''')while 1:
cmd_info=input("[%s]:"%self.user).strip()#put test.py file
cmd_list=cmd_info.split()#默认以空格分隔
if cmd_list[0]=="quit":break
ifhasattr(self,cmd_list[0]):
func=getattr(self,cmd_list[0])
func(*cmd_list)#传输文件的路径必须和客户端路径同级
def put(self,*cmd_list):
action,local_path,target_path=cmd_listprint( action,local_path,target_path)#os.path.join(self.mainPath, local_path):self.mainPath+local_path得到本地完全的图片的路径
#这里首先对格式进行判断
#获取文件完整路径
local_path=os.path.join(self.mainPath,local_path)#获取文件的名字
file_name=os.path.basename(local_path)#获取文件大小
file_size=os.stat(local_path).st_size
data={'action': 'put','file_name':file_name,'file_size':file_size,'target_path':target_path#类型
}
has_sent=0
self.sock.send(json.dumps(data).encode('utf-8'))
is_exist=int(self.sock.recv(1024).decode("utf-8"))if is_exist==800:print(STATUS_CODE[is_exist])
choice=input('请输入你的选择是否选择续传:[Y/N]')if choice.upper()=="Y":
self.sock.sendall("Y".encode("utf8"))
continue_position=self.sock.recv(1024).decode("utf8")
has_sent+=int(continue_position)else:
self.sock.sendall("N".encode("utf8"))elif is_exist==801:print(STATUS_CODE[is_exist])return
else:print(STATUS_CODE[is_exist])
f=open(local_path,"rb")#将光标的位置调整到has_sent
f.seek(has_sent)while has_sent
data=f.read(1024)
self.sock.sendall(data)
has_sent+=len(data)
self.show_progress(has_sent,file_size)
f.close()defshow_progress(self,has,total):
rate=float(has)/float(total)
rate_num=int(rate*100)
sys.stdout.write("%s%% %s\r"%(rate_num,"#"*rate_num))def ls(self,*cmd_list):
data={'action': 'ls',
}
self.sock.sendall((json.dumps(data).encode("utf8")))
reponse=self.sock.recv(1024).decode("utf8")print(reponse)def cd(self,*cmd_list):
data={'action': 'cd','dirname':cmd_list[1]
}
self.sock.sendall((json.dumps(data).encode("utf8")))
reponse= self.sock.recv(1024).decode("utf8")#self.current_dir=reponse
print("当前目录:"+str(reponse))def mkdir(self,*cmd_list):
data={'action': 'mkdir','dirname': cmd_list[1]
}
self.sock.sendall((json.dumps(data).encode("utf8")))
reponse= self.sock.recv(1024).decode("utf8")print(reponse)#身份认证
defauthenticate(self):#如果用户名或者密码有一个为空,则要求再次输入,反之进入下一步
if self.options.username is None or self.options.password isNone:
username=input('username:')
password=input('password')returnself.get_auth_result(username,password)returnself.get_auth_result(self.options.username,self.options.password)#发送认证信息
defget_auth_result(self,username,password):#构建数据
dic={'action':'auth','username':username,'password':password
}#发送认证信息
self.sock.send(json.dumps(dic).encode('utf-8'))#判断服务端回复信息,进行认证
response=self.response()if response['status_code']==254:
self.user=username
self.current_dir=usernameprint(STATUS_CODE[254])else:print(response['status_code']+response['status_mes'])returnTrue#接收信息
defresponse(self):
data= self.sock.recv(1024).decode('utf-8')
data=json.loads(data)returndata
ch=ClientHandler()#用户操作,在用户操作里面会进行身份认证
ch.interactive()