博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
开发一个支持多用户在线的FTP程序
阅读量:5368 次
发布时间:2019-06-15

本文共 31824 字,大约阅读时间需要 106 分钟。

一,项目题目:开发一个支持多用户在线的FTP程序

二,项目要求:

1.用户加密认证2.允许同时多用户登录3.每个用户有自己的家目录 ,且只能访问自己的家目录4.对用户进行磁盘配额,每个用户的可用空间不同5.允许用户在ftp server上随意切换目录6.允许用户查看当前目录下文件7.允许上传和下载文件,保证文件一致性(md5)8.文件传输过程中显示进度条9.附加功能:支持文件的断点续传

三,注意事项: 

基本要求. 完成1,2,3,5,6,7,8 实力选手. 完成 上条 及需求4 ,     大神操作. 完成 9 且项目目录结构良好、代码逻辑清晰,

  

四,项目分析:

1,用户加密认证

  这个肯定需要用到configparser 和hashlib模块,用md5进行加密,服务端与用户端进行交互前,肯定需要进行认证,在服务端进行认证,客户端需要发送用户名及密码,但是为了安全起见,服务端数据库中的密码应该是加密后的密文,客户端登陆认证时也应该发送密文到服务端,服务端接受到密文与数据库中对应的密文进行比较。

  

2,查看自己的当前目录下的文件

  这个只需要写一个dir就ok简单的说,使用configparse模块就可以完成

  

3,文件传输中显示进度条

  下载的进度条比较好实现,我们可以从服务端受到将要下载的文件的大小,  上传的进度条,我们可以利用文件操作的tell()方法,获取当前指针位置(字节)

  

4,小编的主要思路

- 1 对于此项目,最初的想法是写出上传,和下载文件的程序,包括客户端和服务端。-  2 在此基础上扩展程序,包括提出开始程序到bin里面,配置文件在config里面-  3 然后把上传文件和下载文件的程序进行断点续传的程序重构-   4 在此基础上,对文件进行加密-   5 增加功能,包括设置进度条,增加查看功能,增加目录功能,删除文件功能,切换目录功能等-   6 然后再设置磁盘分配功能,完善内容-   7 然后添加用户登陆,包括对用户的密码加密等功能-   8 写完后检查程序

  

 五,项目流程图

 

 

六,README文件

## 作者:zhanzhengrecheng## 版本:示例版本 v0.1## 程序介绍:- 实现了支持多用户在线的FTP程序 常用功能- 功能全部用python的基础知识实现,用到了socket\hashlib\configparse\os\sys\pickle\函数\模块\类知识## 概述本次作业文件夹一共包含了以下5个文件:- 流程图: FTP_homework思路流程图- 程序结构图:整个FTP_homework的程序文件结构- 程序结构文件:整个FTP_homework的程序文件结构- 程序文件: FTP_homework- 程序说明文件:README.md## 程序要求- 1.用户加密认证- 2.允许同时多用户登录- 3.每个用户有自己的家目录 ,且只能访问自己的家目录- 4.对用户进行磁盘配额,每个用户的可用空间不同- 5.允许用户在ftp server上随意切换目录- 6.允许用户查看当前目录下文件- 7.允许上传和下载文件,保证文件一致性(md5)- 8.文件传输过程中显示进度条- 9.附加功能:支持文件的断点续传## 本项目思路- 1 对于此项目,最初的想法是写出上传,和下载文件的程序,包括客户端和服务端。-  2 在此基础上扩展程序,包括提出开始程序到bin里面,配置文件在config里面-  3 然后把上传文件和下载文件的程序进行断点续传的程序重构-   4 在此基础上,对文件进行加密-   5 增加功能,包括设置进度条,增加查看功能,增加目录功能,删除文件功能,切换目录功能等-   6 然后再设置磁盘分配功能,完善内容-   7 然后添加用户登陆,包括对用户的密码加密等功能-   8 写完后检查程序##### 备注(程序结构)> 目前还不会把程序树放在README.md里面,所以做出程序结构的txt版本和图片版本,放在文件外面方便查看## 对几个实例文件的说明### 几个实例文件全是为了上传和下载使用,自己随便找的素材## 不足及其改进的方面### 每次程序从用户登陆到使用只能完成一次功能,不能重复使用## 程序结构│  FTP_homework│  __init__.py│  ├─client                # 客户端程序入口│  │  __init__.py│  ├─bin                # 可执行程序入口目录│  │      run.py│  │      __init__.py│  ├─config             # 配置文件目录│  │  │  settings.py    # 配置文件│  │  │  __init__.py       │  ├─core               # 主要逻辑程序目录│  │  │  ftp_client.py  # client端主程序模块│  │  │  __init__.py       │  ├─download           # 下载内容模块│  │      a.jpg │  │      a.txt│  │      c.mp4  │  └─upload             # 上传内容模块│          a.txt│          aa.avi└─server                 # 服务端程序入口    ├─bin    │      run.py        # 可执行程序入口目录    │      __init__.py     ├─config             # 配置文件目录    │  │  accounts.ini   # 账号密码配置文件    │  │  settings.py    # 配置文件    │  │  __init__.py            ├─core               # 主要逻辑程序目录    │  │  ftp_server.py  # server端主程序模块    │  │  main.py        # 主程序模块    │  │  user_handle.py # 用户注册登录模块      └─home               # 家目录        │  __init__.py        ├─curry          # curry用户的家目录        │  │  aa.avi        │  └─test        └─james           # james用户的家目录            │  a.jpg            │  aa.avi            │  c.mp4            └─test1

  

七,程序结构图

 八,程序代码

1,server

1.1 bin

run.py

# _*_ coding: utf-8 _*_import osimport sysBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))sys.path.append(BASE_DIR)from core import ftp_serverfrom core import mainfrom config import settingsif __name__ == '__main__':    a = main.Manager()    a.interactive()

  

1.2config

settings.py

# _*_ coding: utf-8 _*_ import osimport sysimport socketBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))sys.path.append(BASE_DIR)ACCOUNTS_FILE = os.path.join(BASE_DIR,'config','accounts.ini')address_family = socket.AF_INETsocket_type = socket.SOCK_STREAMBIND_HOST = '127.0.0.1'BIND_PORT = 9999ip_port = (BIND_HOST,BIND_PORT)coding = 'utf-8'listen_count = 5max_recv_bytes = 8192allow_reuser_address = False

  

1.3core

ftp_server.py

# _*_ coding: utf-8 _*_import socketimport structimport jsonimport osimport pickleimport subprocessimport hashlibfrom config import settingsfrom core.user_handle import UserHandleclass FTPServer():    def __init__(self,server_address,bind_and_listen = True):        self.server_address = server_address        self.socket = socket.socket(settings.address_family,settings.socket_type)        if bind_and_listen:            try:                self.server_bind()                self.server_listen()            except Exception:                self.server_close()    def server_bind(self):        allow_reuse_address = False        if allow_reuse_address:            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)        self.socket.bind(self.server_address)    def server_listen(self):        self.socket.listen(settings.listen_count)    def server_close(self):        self.socket.close()    def server_accept(self):        return self.socket.accept()    def conn_close(self, conn):        conn.close()    def getfile_md5(self):        '''获取文件的md5'''        return hashlib.md5(self.readfile()).hexdigest()    def readfile(self):        '''读取文件,得到文件内容的bytes类型'''        with open(self.file_path,'rb') as f:            filedata = f.read()        return filedata    def send_filedata(self,exist_file_size=0):        """下载时,将文件打开,send(data)"""        with open(self.file_path, 'rb') as f:            f.seek(exist_file_size)            while True:                data = f.read(1024)                if data:                    self.conn.send(data)                else:                    break    def get(self, cmds):        '''       下载,首先查看文件是否存在,然后上传文件的报头大小,上传文件,以读的方式发开文件       找到下载的文件    发送 header_size    发送 header_bytes file_size    读文件 rb 发送 send(line)    若文件不存在,发送0 client提示:文件不存在       :param cmds:       :return:               '''        if len(cmds) > 1:            filename = cmds[1]            self.file_path = os.path.join(os.getcwd(), filename)            if os.path.isfile(self.file_path):                file_size = os.path.getsize(self.file_path)                obj = self.conn.recv(4)                exist_file_size = struct.unpack('i', obj)[0]                header = {                    'filename': filename,                    'filemd5': self.getfile_md5(),                    'file_size': file_size                }                header_bytes = pickle.dumps(header)                self.conn.send(struct.pack('i', len(header_bytes)))                self.conn.send(header_bytes)                if exist_file_size:  # 表示之前被下载过 一部分                    if exist_file_size != file_size:                        self.send_filedata(exist_file_size)                    else:                        print('\033[31;1mbreakpoint and file size are the same\033[0m')                else:  # 文件第一次下载                    self.send_filedata()            else:                print('\033[31;1merror\033[0m')                self.conn.send(struct.pack('i', 0))        else:            print("\033[31;1muser does not enter file name\033[0m")    def recursion_file(self, dir):        """递归查询用户目录下的所有文件,算出文件的大小"""        res = os.listdir(dir)        for i in res:            path = os.path.join(dir,i)            if os.path.isdir(path):                self.recursion_file(path)            elif os.path.isfile(path):                self.home_bytes_size += os.path.getsize(path)    def current_home_size(self):        """得到当前用户目录的大小,字节/M"""        self.home_bytes_size =0        self.recursion_file(self.homedir_path)        home_m_size = round(self.home_bytes_size / 1024 / 1024, 1)    def put(self,cmds):        """从client上传文件到server当前工作目录下        """        if len(cmds) >1:            obj = self.conn.recv(4)            state_size = struct.unpack('i', obj)[0]            if state_size ==0:                print("\033[31;1mfile does not exist!\033[0m")            else:                # 算出了home下已被占用的大小self.home_bytes_size                self.current_home_size()                header_bytes = self.conn.recv(struct.unpack('i', self.conn.recv(4))[0])                header_dic = pickle.loads(header_bytes)                filename = header_dic.get('filename')                file_size = header_dic.get('file_size')                file_md5 = header_dic.get('file_md5')                self.file_path = os.path.join(os.getcwd(),filename)                if os.path.exists(self.file_path):                    self.conn.send(struct.pack('i',1))                    has_size = os.path.getsize(self.file_path)                    if has_size == file_size:                        print("\033[31;1mfile already does exist!\033[0m")                        self.conn.send(struct.pack('i', 0))                    else:                        print('\033[31;1mLast file not finished,this time continue\033[0m')                        self.conn.send(struct.pack('i', 1))                        if self.home_bytes_size + int(file_size-has_size)>self.quota_bytes:                            print('\033[31;1mSorry exceeding user quotas\033[0m')                            self.conn.send(struct.pack('i', 0))                        else:                            self.conn.send(struct.pack('i', 1))                            self.conn.send(struct.pack('i', has_size))                            with open(self.file_path, 'ab') as f:                                f.seek(has_size)                                self.write_file(f, has_size, file_size)                            self.verification_filemd5(file_md5)                else:                    self.conn.send(struct.pack('i', 0))                    print('\033[31;1mSorry file does not exist now first put\033[0m')                    if self.home_bytes_size + int(file_size) > self.quota_bytes:                        print('\033[31;1mSorry exceeding user quotas\033[0m')                        self.conn.send(struct.pack('i', 0))                    else:                        self.conn.send(struct.pack('i', 1))                        with open(self.file_path,'wb') as f:                            recv_size = 0                            self.write_file(f, recv_size, file_size)                        self.verification_filemd5(file_md5)        else:            print("\033[31;1muser does not enter file name\033[0m")    def write_file(self,f,recv_size,file_size):        '''上传文件时,将文件内容写入到文件中'''        while recv_size < file_size:            res = self.conn.recv(settings.max_recv_bytes)            f.write(res)            recv_size += len(res)            self.conn.send(struct.pack('i', recv_size))  # 为了进度条的显示    def verification_filemd5(self,filemd5):        # 判断文件内容的md5        if self.getfile_md5() == filemd5:            print('\033[31;1mCongratulations download success\033[0m')            self.conn.send(struct.pack('i', 1))        else:            print('\033[31;1mSorry download failed\033[0m')            self.conn.send(struct.pack('i', 0))    def ls(self,cmds):        '''查看当前工作目录下,先返回文件列表的大小,在返回查询的结果'''        print("\033[34;1mview current working directory\033[0m")        subpro_obj = subprocess.Popen('dir',shell=True,                                      stdout=subprocess.PIPE,                                      stderr=subprocess.PIPE)        stdout = subpro_obj.stdout.read()        stderr = subpro_obj.stderr.read()        self.conn.send(struct.pack('i',len(stdout + stderr)))        self.conn.send(stdout)        self.conn.send(stderr)    def mkdir(self,cmds):        '''增加目录        在当前目录下,增加目录        1.查看目录名是否已经存在        2.增加目录成功,返回 1        2.增加目录失败,返回 0'''        print("\033[34;1madd working directory\033[0m")        if len(cmds) >1:            mkdir_path = os.path.join(os.getcwd(),cmds[1])            if not os.path.exists(mkdir_path):                os.mkdir(mkdir_path)                print('\033[31;1mCongratulations add directory success\033[0m')                self.conn.send(struct.pack('i',1))            else:                print("\033[31;1muser directory already does exist\033[0m")                self.conn.send(struct.pack('i',0))        else:            print("\033[31;1muser does not enter file name\033[0m")    def cd(self,cmds):        '''切换目录        1.查看是否是目录名        2.拿到当前目录,拿到目标目录,        3.判断homedir是否在目标目录内,防止用户越过自己的home目录 eg: ../../....        4.切换成功,返回 1        5.切换失败,返回 0'''        print("\033[34;1mSwitch working directory\033[0m")        if len(cmds) > 1:            dir_path = os.path.join(os.getcwd(),cmds[1])            if os.path.isdir(dir_path):                #os.getcwd 获取当前工作目录                previous_path = os.getcwd()                #os.chdir改变当前脚本目录                os.chdir(dir_path)                target_dir = os.getcwd()                if self.homedir_path in target_dir:                    print('\033[31;1mCongratulations switch directory success\033[0m')                    self.conn.send(struct.pack('i', 1))                else:                    print('\033[31;1mSorry switch directory failed\033[0m')                    # 切换失败后,返回到之前的目录下                    os.chdir(previous_path)                    self.conn.send(struct.pack('i', 0))            else:                print('\033[31;1mSorry switch directory failed,the directory is not current directory\033[0m')                self.conn.send(struct.pack('i', 0))        else:            print("\033[31;1muser does not enter file name\033[0m")    def remove(self,cmds):        """删除指定的文件,或者空文件夹               1.删除成功,返回 1               2.删除失败,返回 0               """        print("\033[34;1mRemove working directory\033[0m")        if len(cmds) > 1:            file_name = cmds[1]            file_path = os.path.join(os.getcwd(),file_name)            if os.path.isfile(file_path):                os.remove(file_path)                self.conn.send(struct.pack('i', 1))            elif os.path.isdir(file_path):  # 删除空目录                if not len(os.listdir(file_path)):                    os.removedirs(file_path)                    print('\033[31;1mCongratulations remove success\033[0m')                    self.conn.send(struct.pack('i', 1))                else:                    print('\033[31;1mSorry remove directory failed\033[0m')                    self.conn.send(struct.pack('i', 0))            else:                print('\033[31;1mSorry remove directory failed\033[0m')                self.conn.send(struct.pack('i', 0))        else:            print("\033[31;1muser does not enter file name\033[0m")    def get_recv(self):        '''从client端接收发来的数据'''        return pickle.loads(self.conn.recv(settings.max_recv_bytes ))    def handle_data(self):        '''处理接收到的数据,主要是将密码转化为md5的形式'''        user_dic = self.get_recv()        username = user_dic['username']        password = user_dic['password']        md5_obj = hashlib.md5()        md5_obj.update(password)        check_password = md5_obj.hexdigest()    def auth(self):        '''        处理用户的认证请求        1,根据username读取accounts.ini文件,然后查看用户是否存在        2,将程序运行的目录从bin.user_auth修改到用户home/username方便之后查询        3,把客户端返回用户的详细信息        :return:        '''        while True:            user_dic = self.get_recv()            username = user_dic['username']            password = user_dic['password']            md5_obj = hashlib.md5(password.encode('utf-8'))            check_password = md5_obj.hexdigest()            user_handle  = UserHandle(username)            # 判断用户是否存在 返回列表,            user_data = user_handle.judge_user()            if user_data:                if user_data[0][1] ==check_password:                    self.conn.send(struct.pack('i',1))  # 登录成功返回 1                    self.homedir_path = os.path.join(settings.BASE_DIR,'home',username)                    # 将程序运行的目录名修改到 用户home目录下                    os.chdir(self.homedir_path)                    # 将用户配额的大小从M 改到字节                    self.quota_bytes = int(user_data[2][1])*1024*1024                    user_info_dic = {                        'username':username,                        'homedir':user_data[1][1],                        'quota':user_data[2][1]                    }                    # 用户的详细信息发送到客户端                    self.conn.send(pickle.dumps(user_info_dic))                    return True                else:                    self.conn.send(struct.pack('i', 0))  # 登录失败返回 0            else:                self.conn.send(struct.pack('i', 0))  # 登录失败返回 0    def server_link(self):        print("\033[31;1mwaiting client .....\033[0m")        while True:  # 链接循环            self.conn,self.client_addr = self.server_accept()            while True:  # 通信循环                try:                    self.server_handle()                except Exception:                    break            self.conn_close(self.conn)    def server_handle(self):        '''处理与用户的交互指令'''        if self.auth():            print("\033[32;1m-------user authentication successfully-------\033[0m")            res = self.conn.recv(settings.max_recv_bytes)            # 解析命令,提取相应的参数            cmds = res.decode(settings.coding).split()            if hasattr(self, cmds[0]):                func = getattr(self, cmds[0])                func(cmds)

  

main.py

# _*_ coding: utf-8 _*_ from core.user_handle import UserHandlefrom core.ftp_server import FTPServerfrom config import settingsclass Manager():    '''    主程序,包括启动server,创建用户,退出    :return:    '''    def start_ftp(self):        '''启动server端'''        server = FTPServer(settings.ip_port)        server.server_link()        server.close()    def create_user(self):        '''创建用户,执行创建用户的类'''        username =  input("\033[32;1mplease input your username>>>\033[0m").strip()        UserHandle(username).add_user()    def logout(self):        '''        退出登陆        :return:        '''        print("\033[32;1m-------Looking forward to your next login-------\033[0m")        exit()    def interactive(self):        '''交互函数'''        msg = '''\033[32;1m                       1   启动ftp服务端                       2   创建用户                       3   退出               \033[0m'''        menu_dic = {            "1": 'start_ftp',            "2": 'create_user',            "3": 'logout',        }        exit_flag = False        while not exit_flag:            print(msg)            user_choice = input("Please input a command>>>").strip()            if user_choice in menu_dic:                getattr(self,menu_dic[user_choice])()            else:                print("\033[31;1myou choice doesn't exist\033[0m")

  

user_handle.py

# _*_ coding: utf-8 _*_ import configparserimport hashlibimport osfrom config import settingsclass UserHandle():    '''    创建用户名称,密码    如果用户存在,则返回,如果用户不存在,则注册成功    '''    def __init__(self,username):        self.username = username        self.config = configparser.ConfigParser()        self.config.read(settings.ACCOUNTS_FILE)    @property    def password(self):        '''生成用户的默认密码 '''        password_inp = input("\033[32;1mplease input your password>>>\033[0m").strip()        md5_obj = hashlib.md5()        md5_obj.update(password_inp.encode())        md5_password = md5_obj.hexdigest()        return md5_password    @property    def disk_quota(self):        '''生成每个用户的磁盘配额'''        quota = input('\033[32;1mplease input Disk quotas>>>:\033[0m').strip()        if quota.isdigit():            return quota        else:            exit('\033[31;1mdisk quotas must be integer\033[0m')    def add_user(self):        """创建用户,存到accounts.ini"""        if not self.config.has_section(self.username):            print('\033[31;1mcreating username is :%s \033[0m' %self.username)            self.config.add_section(self.username)            self.config.set(self.username, 'password', self.password)            self.config.set(self.username, 'homedir', 'home/' + self.username)            self.config.set(self.username, 'quota', self.disk_quota)            with open(settings.ACCOUNTS_FILE, 'w') as f:                self.config.write(f)            os.mkdir(os.path.join(settings.BASE_DIR, 'home', self.username))  # 创建用户的home文件夹            print('\033[1;32msuccessfully create userdata\033[0m')        else:            print('\033[1;31musername already existing\033[0m')    def judge_user(self):        """判断用户是否存在"""        if self.config.has_section(self.username):            return self.config.items(self.username)

  

2,client

2.1bin

run.py

# _*_ coding: utf-8 _*_import osimport sysBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))sys.path.append(BASE_DIR)from core import ftp_clientfrom config import settingsif __name__ == '__main__':    run = ftp_client.FTPClient(settings.ip_port)    run.execute()

  

2.2config

settings.py

# _*_ coding: utf-8 _*_import osimport sysimport socketBASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))sys.path.append(BASE_DIR)# 下载的文件存放路径down_filepath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'download')# 上传的文件存放路径upload_filepath = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'upload')#绑定的IP地址BIND_HOST = '127.0.0.1'#绑定的端口号BIND_PORT = 9999ip_port = (BIND_HOST,BIND_PORT)address_family = socket.AF_INETsocket_type = socket.SOCK_STREAMcoding = 'utf-8'listen_count = 5max_recv_bytes = 8192allow_reuser_address = False

  

2.3core

ftp_client.py

# _*_ coding: utf-8 _*_ import socketimport structimport jsonimport osimport sysimport pickleimport hashlibfrom config import settingsclass FTPClient:    def __init__(self,server_address,connect = True):        self.server_address = server_address        self.socket = socket.socket(settings.address_family,settings.socket_type)        if connect:            try:                self.client_connect()            except Exception:                self.client_close()    def client_connect(self):        try:            self.socket.connect(self.server_address)        except Exception as e:            print("\033[31;1merror:%s\033[0m"%e)            exit("\033[31;1m\nThe server is not activated \033[0m")    def client_close(self):        self.socket.close()    def readfile(self):        '''读取文件'''        with open(self.file_path,'rb') as f:            filedata = f.read()        return filedata    def appendfile_content(self,file_path,temp_file_size,file_size):        '''追加文件内容'''        with open(file_path,'ab') as f:            f.seek(temp_file_size)            get_size = temp_file_size            while get_size < file_size:                res = self.socket.recv(settings.max_recv_bytes)                f.write(res)                get_size += len(res)                self.progress_bar(1,get_size,file_size)  #1表示下载    def getfile_md5(self):        '''对文件内容进行加密,也就是保持文件的一致性'''        md5 = hashlib.md5(self.readfile())        print("md5是:\n",md5.hexdigest())        return md5.hexdigest()    def progress_bar(self,num,get_size,file_size):        float_rate = float(get_size) / float(file_size)        rate_num = round(float_rate * 100,2)        if num ==1: #1表示下载            sys.stdout.write('\033[31;1m\rfinish downloaded perentage:{0}%\033[0m'.format(rate_num))        elif num ==2:  #2表示上传            sys.stdout.write('\033[31;1m\rfinish uploaded perentage:{0}%\033[0m'.format(rate_num))        sys.stdout.flush()    def recv_file_header(self,header_size):        """接收文件的header, filename file_size file_md5"""        header_types = self.socket.recv(header_size)        header_dic = pickle.loads(header_types)        print(header_dic, type(header_dic))        total_size = header_dic['file_size']        filename = header_dic['filename']        filemd5 = header_dic['filemd5']        return (filename,total_size,filemd5)    def verification_filemd5(self,filemd5):        # 判断下载下来的文件MD5值和server传过来的MD5值是否一致        if self.getfile_md5() == filemd5:            print('\033[31;1mCongratulations download success\033[0m')        else:            print('\033[31;1mSorry download failed,download again support breakpoint continuation\033[0m')    def write_file(self,f,get_size,file_size):        '''下载文件,将内容写入文件中'''        while get_size < file_size:            res = self.socket.recv(settings.max_recv_bytes)            f.write(res)            get_size += len(res)            self.progress_bar(1,get_size,file_size)  #1表示下载    def get(self,cmds):        """从server下载文件到client        """        if len(cmds) >1:            filename = cmds[1]            self.file_path = os.path.join(settings.down_filepath, filename)            if os.path.isfile(self.file_path):  #如果文件存在,支持断电续传                temp_file_size = os.path.getsize(self.file_path)                self.socket.send(struct.pack('i',temp_file_size))                header_size = struct.unpack('i',self.socket.recv(4))[0]                if header_size:                    filename,file_size,filemd5 = self.recv_file_header(header_size)                    if temp_file_size == file_size:                        print('\033[34;1mFile already does exist\033[0m')                    else:                        print('\033[34;1mFile now is breakpoint continuation\033[0m')                        self.appendfile_content(self.file_path,temp_file_size)                        self.verification_filemd5(filemd5)                else:                    print("\033[34;1mFile was downloaded before,but now server's file is not exist\033[0m")            else:#如果文件不存在,则是直接下载                self.socket.send(struct.pack('i',0))                obj = self.socket.recv(1024)                header_size = struct.unpack('i', obj)[0]                if header_size==0:                    print("\033[31;1mfile does not exist!\033[0m")                else:                    filename, file_size, filemd5 = self.recv_file_header(header_size)                    download_filepath = os.path.join(settings.down_filepath, filename)                    with open(download_filepath, 'wb') as f:                        get_size = 0                        self.write_file(f, get_size, file_size)                    self.verification_filemd5(filemd5)        else:            print("\033[31;1muser does not enter file name\033[0m")    def ls(self,cmds):        '''查看当前工作目录,文件列表'''        print("\033[34;1mview current working directory\033[0m")        obj = self.socket.recv(4)        dir_size = struct.unpack('i',obj)[0]        recv_size = 0        recv_bytes = b''        while recv_size 
1: obj = self.socket.recv(4) res = struct.unpack('i', obj)[0] if res: print('\033[31;1mCongratulations switch directory success\033[0m') else: print('\033[31;1mSorry switch directory failed\033[0m') else: print("\033[31;1muser does not enter file name\033[0m") def remove(self,cmds): '''表示删除文件或空文件夹''' print("\033[34;1mRemove working directory\033[0m") obj = self.socket.recv(4) res = struct.unpack('i', obj)[0] if res: print('\033[31;1mCongratulations remove success\033[0m') else: print('\033[31;1mSorry remove directory failed\033[0m') def open_sendfile(self,file_size,recv_size =0): '''打开要上传的文件(由于本程序上传文件的原理是先读取本地文件,再写到上传地址的文件)''' with open(self.file_path, 'rb') as f: # send_bytes = b'' # send_size = 0 f.seek(recv_size) while True: data = f.read(1024) if data: self.socket.send(data) obj = self.socket.recv(4) recv_size = struct.unpack('i', obj)[0] self.progress_bar(2, recv_size, file_size) else: break success_state = struct.unpack('i', self.socket.recv(4))[0] if success_state: print('\033[31;1mCongratulations upload success\033[0m') else: print('\033[31;1mSorry upload directory failed\033[0m') def put_situation(self,file_size,condition=0): '''上传的时候有两种情况,文件已经存在,文件不存在''' quota_state= struct.unpack('i', self.socket.recv(4))[0] if quota_state: if condition: obj = self.socket.recv(4) recv_size = struct.unpack('i', obj)[0] self.open_sendfile(file_size,recv_size) else: self.open_sendfile(file_size) else: print('\033[31;1mSorry exceeding user quotas\033[0m') def put(self,cmds): """往server端登录的用户目录下上传文件 """ if len(cmds) > 1: filename = cmds[1] self.file_path = os.path.join(settings.upload_filepath, filename) if os.path.isfile(self.file_path): # 如果文件存在,支持断电续传 self.socket.send(struct.pack('i', 1)) file_size = os.path.getsize(self.file_path) header_dic = { 'filename': os.path.basename(filename), 'file_md5': self.getfile_md5(), 'file_size': file_size } header_bytes = pickle.dumps(header_dic) self.socket.send(struct.pack('i', len(header_bytes))) self.socket.send(header_bytes) state = struct.unpack('i', self.socket.recv(4))[0] if state: #已经存在 has_state = struct.unpack('i', self.socket.recv(4))[0] if has_state: self.put_situation(file_size, 1) else: # 存在的大小 和文件大小一致 不必再传 print("\033[31;1mfile already does exist!\033[0m") else: # 第一次传 self.put_situation(file_size) else: # 文件不存在 print("\033[31;1mfile does not exist!\033[0m") self.socket.send(struct.pack('i', 0)) else: print("\033[31;1muser does not enter file name\033[0m") def get_recv(self): '''从client端接受发来的数据''' return pickle.loads(self.socket.recv(settings.max_recv_bytes)) def login(self): ''' 登陆函数,当登陆失败超过三次,则退出 用户密码发送到server短 接受server端返回的信息,如果成功返回1,失败返回0 :return: 如果用户账号密码正确,则返回用户数据的字典 ''' retry_count = 0 while retry_count <3: username = input('\033[34;1mplease input Username:\033[0m').strip() if not username: continue password = input('\033[34;1mplease input Password:\033[0m').strip() user_dic = { 'username':username, 'password':password } #将用户信息发送到客户端,然后接受客户端的数据 data = pickle.dumps(user_dic) self.socket.send(pickle.dumps(user_dic)) #为了防止出现黏包问题,所以先解压报头,读取报头,再读数据 obj = self.socket.recv(4) res = struct.unpack('i',obj)[0] #此处,如果返回的是代码4001,则成功 4002则失败 if res: print("\033[32;1m-----------------welcome to ftp client-------------------\033[0m") user_info_dic = self.get_recv() recv_username = user_info_dic['username'] return True else: print("\033[31;1mAccount or Passwordoes not correct!\033[0m") retry_count +=1 def execute(self): ''' 执行,或者实施 :return: ''' if self.login(): while True: try: self.help_info() inp = input("Please input a command>>>").strip() if not inp: continue self.socket.send(inp.encode(settings.coding)) cmds = inp.split() if hasattr(self, cmds[0]): func = getattr(self, cmds[0]) func(cmds) break else: print('\033[31;1mNo such command ,please try again\033[0m') except Exception as e: # server关闭了 print('\033[31;1m%s\033[0m'%e) break def help_info(self): print ('''\033[34;1m get + (文件名) 表示下载文件 put + (文件名) 表示上传文件 ls 表示查询当前目录下的文件列表(只能访问自己的文件列表) mkdir + (文件名) 表示创建文件夹 cd + (文件名) 表示切换目录(只能在自己的文件列表中切换) remove + (文件名) 表示删除文件或空文件夹 \033[0m''')

  

转载于:https://www.cnblogs.com/wj-1314/p/8707787.html

你可能感兴趣的文章
PKUWC2018 5/6
查看>>
As-If-Serial 理解
查看>>
洛谷P1005 矩阵取数游戏
查看>>
在Silverlight中使用HierarchicalDataTemplate为TreeView实现递归树状结构
查看>>
无线通信基础(一):无线网络演进
查看>>
关于python中带下划线的变量和函数 的意义
查看>>
linux清空日志文件内容 (转)
查看>>
Ajax : load()
查看>>
MySQL-EXPLAIN执行计划Extra解释
查看>>
图片点击轮播(三)-----2017-04-05
查看>>
直播技术细节3
查看>>
java中new一个对象和对象=null有什么区别
查看>>
字母和数字键的键码值(keyCode)
查看>>
01_1_准备ibatis环境
查看>>
JavaScript中的BOM和DOM
查看>>
spring注入Properties
查看>>
jmeter(五)创建web测试计划
查看>>
1305: [CQOI2009]dance跳舞 - BZOJ
查看>>
将html代码中的大写标签转换成小写标签
查看>>
jmeter多线程组间的参数传递
查看>>