python 网络编程 练习(一)---FTP

在这里插入图片描述

#ftp_client.py:

import optparse,socket,json,os,sys

STATUS_CODE={
    250:"invalid cmd format",
    251:"invalid cmd",
    252:"invalid auth data",
    253:"wrong username or pwd",
    254:"passed authentication",
    255:"filename doesn't provided",
    256:"file doesn't exist",
    257:"ready to send file",
    258:"md5 verification",
    800:"the file exist,but not enough",
    801:"the file exist",
    802:"ready to receive data",
    900:"md5 valdate success"
}

class ClientHandler():
    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", "--pwd", dest="pwd")
        # 在终端中输入python ftp_server.py -s 127.0.0.1 -P 8090 yyy uuu 8989
        self.options,self.args=self.op.parse_args()
        # options为{"FTP_server":"127.0.0.1","port":"8090"},类型为optparse.Values
        # args为["yyy","uuu","8989"]
        self.verify_args(self.options,self.args)
        self.make_connection()
        self.mainPath=os.path.dirname(os.path.abspath(__file__))
        self.last=0

    def verify_args(self,options,args):#验证用户输入的参数
        server=options.server# options不是dict,而是optparse.Values,不能通过键取值
        port=options.port
        username=options.username
        pwd=options.pwd
        if int(port)>0 and int(port)<65535:
            return True
        else:
            exit("The port should in (0,65535)")

    def make_connection(self):#进行连接
        self.sock=socket.socket()
        self.sock.connect((self.options.server,int(self.options.port)))

    def interactive(self):#进行交互,选择要进行什么操作
        print("begin")
        if self.authenticate():
            while True:
                cmd_info=input("[%s]"%self.current_dir).strip()
                cmd_list=cmd_info.split()#默认按空格分割
                if hasattr(self,cmd_list[0]):
                    func=getattr(self,cmd_list[0])
                    func(*cmd_list)

    def put(self,*cmd_list):#上传文件
        action,local_path,target_path=cmd_list#分别为要进行的操作(put),在client的路径,在server的路径
        local_path=os.path.join(self.mainPath,local_path)
        filename=os.path.basename(local_path)#文件名(不包括路径)
        file_size=os.stat(local_path).st_size#文件大小
        data={"action":"put","filename":filename,"file_size":file_size,"target_path":target_path}
        self.sock.send(json.dumps(data).encode("utf-8"))
        is_exist=int(self.sock.recv(1024).decode("utf-8"))
        has_sent=0
        if is_exist==800:#断点续传
            choice=input("the file exist,but is not complete,continue or not?[Y/N]").strip()
            if choice=="Y" or choice=="y":#断点续传
                self.sock.sendall("Y".encode("utf-8"))
                continue_position=int(self.sock.recv(1024))
                has_sent+=continue_position
            else:#重新上传
                self.sock.sendall("N".encode("utf-8"))
        elif is_exist==801:#已完成上传
            print("the file exist")
            return "OK"#结束上传操作
        elif is_exist==802:#文件未上传,删掉这2行也可以
            pass
        f=open(local_path,"rb")
        f.seek(has_sent)#将光标移动的已发送部分后,之后从这里开始继续读取
        while has_sent<file_size:
            data=f.read(1024)
            self.sock.sendall(data)
            has_sent+=len(data)
            self.show_progress(has_sent,file_size)
        f.close()
        print("upload succeed")

    def authenticate(self):#进行验证
        if self.options.username is None or self.options.pwd is None:#判断用户名和密码是否为空
            username=input("username:")
            pwd=input("password:")
            return self.get_auth_result(username,pwd)
        return self.get_auth_result(self.options.username,self.options.pwd)

    def response(self):#将server返回的信息转换成str
        data=self.sock.recv(1024).decode("utf-8")
        data=json.loads(data)
        return data

    def get_auth_result(self,user,pwd):#真正进行身份验证的函数
        data={"action":"auth","username":user,"pwd":int(pwd)}
        self.sock.send(json.dumps(data).encode("utf-8"))
        response=self.response()
        if response["status_code"]==254:
            self.user=user
            self.current_dir=user
            print(STATUS_CODE[254])
            return True
        else:
            print(STATUS_CODE[response["status_code"]])

    def show_progress(self,has,total):#打印进度条
        rate=float(has)/float(total)
        rate_num=int(rate*100)#已完成的百分比
        sys.stdout.write("%s%% %s\r"%(rate_num,"#"*rate_num))#输出进度条
        self.last=rate_num

    def ls(self,*cmd_list):#显示当前目录下的文件和文件夹
        data={"action":"ls"}
        self.sock.sendall(json.dumps(data).encode("utf-8"))
        result=self.sock.recv(1024).decode("utf-8")
        print(result)

    def cd(self,*cmd_list):#切换目录
        data={"action":"cd","dirname":cmd_list[1]}
        self.sock.sendall(json.dumps(data).encode("utf-8"))
        result=self.sock.recv(1024).decode("utf-8")
        self.current_dir=os.path.basename(result)#这样只显示最后1级目录

    def mkdir(self,*cmd_list):
        data={"action":"mkdir","dirname":cmd_list[1]}
        self.sock.sendall(json.dumps(data).encode("utf-8"))
        result=self.sock.recv(1024).decode("utf-8")
        print(result)

ch=ClientHandler()
ch.interactive()
#ftp_server.py:

import os,sys

PATH=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)#添加server文件夹到环境变量

from core import main

if __name__=="__main__":
    print("FTP_Server start")
    main.ArgvHandler()
#settings.py:

import os,sys
BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

IP="127.0.0.1"
PORT=8080

ACCOUNT_PATH=os.path.join(BASE_DIR,"conf","accounts.cfg")
#accounts.cfg:

[DEFAULT]

[root]
Password=123
Quotation=100

[han]
Password=111
Quotation=101
#main.py:

import optparse,socketserver
from conf import settings
from core import server

class ArgvHandler():
    def __init__(self):
        self.op=optparse.OptionParser()
        options,args=self.op.parse_args()
        self.verify_args(options,args)

    def verify_args(self,options,args):
        cmd=args[0]
        if hasattr(self,cmd):#通过反射判断示例是否有该方法
            func=getattr(self,cmd)#通过反射获得实例的方法
            func()

    def start(self):
        s=socketserver.ThreadingTCPServer((settings.IP,settings.PORT),server.ServerHandler)
        s.serve_forever()

    def help(self):
        pass
#server.py:

import socketserver,json,configparser,os
from conf import settings

STATUS_CODE={
    250:"invalid cmd format",
    251:"invalid cmd",
    252:"invalid auth data",
    253:"wrong username or pwd",
    254:"passed authentication",
    255:"filename doesn't provided",
    256:"file doesn't exist",
    257:"ready to send file",
    258:"md5 verification",
    800:"the file exist,but not enough",
    801:"the file exist",
    802:"ready to receive data",
    900:"md5 valdate success"
}

class ServerHandler(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            data=self.request.recv(1024).strip()#self.request相当于conn
            data=json.loads(data.decode("utf-8"))#dict,如{"action":"auth","username":"A","pwd":123}
            if data.get("action"):
                if hasattr(self,data.get("action")):
                    func=getattr(self,data.get("action"))
                    func(**data)
                else:
                    print("No such command")
            else:
                print("Invalid command")

    def send_response(self,state_code):
        response={"status_code":state_code}
        self.request.sendall(json.dumps(response).encode("utf-8"))

    def auth(self,**data):#用于验证用户身份
        username=data["username"]
        pwd=data["pwd"]
        user=self.authenticate(username,pwd)#如果验证失败,返回None
        if user:
            self.send_response(254)
        else:
            self.send_response(253)

    def put(self,**data):
        filename=data.get("filename")
        file_size=data.get("file_size")
        target_path=data.get("target_path")
        abs_path=os.path.join(self.mainPath,target_path,filename)#文件在server中的路径,仅支持以用户的家目录为基准的相对路径
        has_received=0
        if os.path.exists(abs_path):#如果该文件已经存在
            file_has_size=os.stat(abs_path).st_size
            if file_has_size<file_size:#如果尚未上传完成(断点续传)
                self.request.sendall("800".encode("utf-8"))
                choice=self.request.recv(1024).decode("utf-8")
                if choice=="Y":#如果选择断点续传
                    self.request.sendall(str(file_has_size).encode("utf-8"))#告知client文件已上传部分的大小
                    has_received+=file_has_size
                    f=open(abs_path,"ab")
                else:#如果选择重新重新上传
                    f = open(abs_path,"wb")#赋给原有的文件
            else:#如果已经上传完成
                self.request.sendall("801".encode("utf-8"))
                return "OK"#结束上传操作
        else:#如果该文件不存在
            print(802)
            self.request.sendall("802".encode("utf-8"))
            f=open(abs_path,"wb")#此时以wb默认打开
        while has_received<file_size:#如果文件还未完全上传
            try:
                data=self.request.recv(1024)
                f.write(data)
                has_received+=len(data)
            except Exception as e:
                print(e)
                break
        f.close()
        print("upload succeed")

    def authenticate(self,user,pwd):#验证身份
        cfg=configparser.ConfigParser()
        cfg.read(settings.ACCOUNT_PATH)
        if user in cfg.sections():#如果有该用户
            if int(cfg[user]["Password"])==pwd:#如果密码正确
                self.user=user#登陆成功后绑定用户名
                self.mainPath=os.path.join(settings.BASE_DIR,"home",self.user)
                return user

    def ls(self,**data):#显示当前目录下的文件和文件夹
        file_list=os.listdir(self.mainPath)
        file_string="\n".join(file_list)
        if not len(file_string):#如果当前目录下为空
            file_string="no file or package"
        self.request.sendall(file_string.encode("utf-8"))

    def cd(self,**data):#切换目录
        dirname=data.get("dirname")
        if dirname=="..":#父目录
            self.mainPath=os.path.dirname(self.mainPath)
        else:
            self.mainPath=os.path.join(self.mainPath,dirname)
        self.request.sendall(self.mainPath.encode("utf-8"))

    def mkdir(self,**data):
        dirname=data.get("dirname")
        path=os.path.join(self.mainPath,dirname)#仅支持以该用户的家目录为基准的相对路径
        if not os.path.exists(path):
            if "/" in dirname:
                os.makedirs(path)
            else:
                os.mkdir(path)
            self.request.sendall(("create %s successfully"%dirname).encode("utf-8"))
        else:
            self.request.sendall("dir exist already".encode("utf-8"))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值