直接上代码吧
整个程序包目录结构如下:
FTP:
ftp_client
client.py
ftp_server
bin
start_server.py
conf
settings.py
core
main.py
server.py
home
jack
logger
ftp_server
1.start_server.py
import os,sys
PATH=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
from core import main
if __name__ == '__main__':
main.ArgvHandler()
2.main.py
import optparse
import socketserver
import os
BASEPATH=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
from conf import settings
from core import server
class ArgvHandler():
def __init__(self):
self.op=optparse.OptionParser()
options,args=self.op.parse_args()
self.verification(options,args)
def verification(self,options,args):
if hasattr(self,args[0]):
func=getattr(self,args[0])
func()
def start(self):
s=socketserver.ThreadingTCPServer((settings.IP,settings.PORT),server.Myserver)
print('服务端开始运行...')
s.serve_forever()
def stop(self):
pass
3.server.py
import socketserver
import json
import os
user_pwd={
'username':'jack',
'password':'123'
}
class Myserver(socketserver.BaseRequestHandler):
def handle(self):
while True:
data=self.request.recv(1024).decode('utf-8')
data=json.loads(data)
#拿到了字典
if hasattr(self,data['action']):
func=getattr(self,data['action'])
func(**data)
def put(self,**data):
filesize=data['filesize']
filepath=os.path.join(data['targetpath'],data['filename'])
has_size=0
if os.path.exists(filepath):
now_has_size=os.stat(filepath).st_size
if now_has_size < filesize:
#断点续传
self.request.sendall('please_choice'.encode('utf-8'))
choice=self.request.recv(1024).decode('utf-8')
if choice == 'Y':
self.request.sendall(str(now_has_size).encode('utf-8'))
has_size+=now_has_size
f=open(filepath,'ab')
else:
f = open(filepath, 'wb')
else:
self.request.sendall('ok'.encode('utf-8'))
else:
self.request.sendall('ready'.encode('utf-8'))
f=open(filepath,'wb')
while has_size < filesize:
try:
file_data=self.request.recv(1024)
except Exception as e:
break
f.write(file_data)
has_size+=len(file_data)
f.close()
def auth(self,**data):
if data['username'] == user_pwd['username'] and data['password'] == user_pwd['password']:
self.request.sendall('101'.encode('utf-8'))
else:
self.request.sendall('102'.encode('utf-8'))
4.settings.py
IP='127.0.0.1'
PORT=8080
ftp_client
1.client.py
import socket
import optparse
import sys,os
import json
import time
BASEPATH=os.path.dirname(os.path.abspath(__file__))
ServerPath=os.path.join(os.path.dirname(BASEPATH),'ftp_server')
FilePath=os.path.join(BASEPATH,'a.txt')
ip_port=('127.0.0.1',8080)
class Myclient:
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()
self.verity_port(self.options,self.args)
self.connection()
def verity_port(self,options,args):
port=options.port
if int(port)>0 and int(port)<65535:
return True
else:
exit('端口不合法')
def connection(self):
print('客户端连接成功...')
self.sock=socket.socket()
self.sock.connect(ip_port)
def auth(self):
if self.authenticate():
while True:
cmd_msg=input('[%s]:' %self.user).strip()
if not cmd_msg:continue
if cmd_msg == 'quit':break
cmd_list=cmd_msg.split()
if hasattr(self,cmd_list[0]):
func=getattr(self,cmd_list[0])
func(*cmd_list)
def put(self,*cmd_list):
cmd_name,localfile,targetpath=cmd_list
targetpath=os.path.join(ServerPath, 'home', self.user)
filesize=os.stat(FilePath).st_size
cmd_data={
'action':cmd_name,
'filename':localfile,
'filesize':filesize,
'targetpath':targetpath
}
# print(cmd_data['filename'],type(filesize),cmd_data['targetpath'])
self.sock.sendall(json.dumps(cmd_data).encode('utf-8'))
confirm_msg=self.sock.recv(1024).decode('utf-8')
has_send=0
if confirm_msg == 'ok':
print('文件已存在,不需要上传了...')
return
elif confirm_msg == 'ready':
print('开始全量上传...')
elif confirm_msg == 'please_choice':
choice=input('选择增量[y]还是全量[n]:[Y/N]').upper()
if choice == 'Y':
self.sock.sendall(choice.encode('utf-8'))
choice_msg=self.sock.recv(1024).decode('utf-8')
print('开始断点续传...')
has_send = int(choice_msg)
else:
self.sock.sendall(choice.encode('utf-8'))
print('开始整包覆盖...')
f=open(FilePath, 'rb')
f.seek(has_send)
while has_send < filesize:
file_data=f.read(1)
self.sock.sendall(file_data)
has_send+=len(file_data)
self.show_process(has_send, filesize)
f.close()
print('传输结束')
return
def show_process(self,has,total):
procent=int((has/total)*100)
# print(procent)
# sys.stdout.write('%s%%')
sys.stdout.write('\r%s%% %s' %(procent,'#'*procent))
time.sleep(0.1)
sys.stdout.flush()
def authenticate(self):
username=self.options.username
password=self.options.password
if username is None or password is None:
username=input('username:').strip()
password=input('password:').strip()
return self.verify_login(username, password)
return self.verify_login(username,password)
def verify_login(self,user,pwd):
login_data={
'action':'auth',
'username':user,
'password':pwd
}
login_data=json.dumps(login_data)
self.sock.sendall(login_data.encode('utf-8'))
verify_login_result=self.sock.recv(1024).decode('utf-8')
if verify_login_result == '101':
self.user=user
print('登录成功')
return True
else:
print('用户名或密码错误,请重新输入!')
return self.authenticate()
mc=Myclient()
mc.auth()
上面这段代码基本实现了登录验证、上传文件(整包上传和断点续传)以及进度条,但是不够完善,有些bug,比如重复执行put a.txt jack的命令会报错。
我没有找到为啥会这个样子,有待以后再找bug了。或者如有高手,请予赐教!