python 实现简单的FTP

一、开发环境

server端:centos 7  python-3.6.2

客户端:Windows 7 python-3.6.2 pycharm-2018

程序目的:1、学习使用socketserver实现并发处理多个客户端。

             2、了解使用struct解决TCP粘包。

二、程序设计

(本人菜鸟一枚,对于开发规范,接口设计完全不懂,完全是随心所欲,自娱自乐。写博客主要是记录自己学习的点点滴滴,如有不足之处还请见谅。)

1、server端

1.1 目录结构如下:

 1.2 目录简介:

FTP_SERVER:程序主目录

app:程序主逻辑目录,目录下有四个模块:

FTPserver.py:FTP  Server端启动入口。

login.py:认证注册模块,用于处理用户注册,登录认证。

dataAnalysis.py:命令解析模块,负责解析,执行客户端命令。

FileOpertion.py:负责文件读,写。数据发送,数据接收。

db:存放user_pwd.db文件,用于存放用户信息(用户名,密码,FTP目录总空间,已使用空间等)

lib:存放公共数据。

1.3 模块中类的继承关系

1.4 执行流程

1.4.1 程序启动文件FTPserver.py,程序启动后进入监听状态。核心代码如下:

class MyFtpServer(socketserver.BaseRequestHandler):

    def handle(self):  # 重写handle方法,处理socket请求
        print(f"连接来自{self.client_address}的客户端")
        commom_obj = Commom()
        data_analy = DataAnalysis()
        login_obj = Login()
        while 1:
            # 执行用户选项:1、登陆系统 2、注册账号。并返回一个结果
            status_id = login_obj.run_client_choice(self.request, commom_obj)
            if status_id == "01":  # 登陆成功
                if not self.run_ftp_server(data_analy,commom_obj):  # 执行ftpserver主功能
                    break
            elif int(status_id) == -1: # client断开连接了
                break
        print(f"客户端{self.client_address}断开了连接")

    def run_ftp_server(self,data_analy,commom_obj):
        """"
        登陆成功后,接收客户端发来的命令,并进行处理
        :param data_analy:负责解析,执行客户端命令的对象
        :param commom_obj:程序执行时所需的数据对象
        :return 返回false代表客户端断开连接了
        """
        while True:
            try:
                cmd_len_pack = self.request.recv(4)
                cmd_len = struct.unpack(‘i‘,cmd_len_pack)[0] # 获取命令长度,防止粘包
            except Exception:
                break
            recv_data = self.request.recv(cmd_len).decode(‘utf-8‘)  # 接收客户端数据
            if recv_data.upper() == "Q":  # 客户端提出断开连接了
                break
            # 解析,处理客户端的命令
            data_analy.syntax_analysis(recv_data, self.request, commom_obj)
        return False

if __name__ == ‘__main__‘:
    print(‘运行FTP服务‘)
    ip_port = (‘192.168.10.10‘,9000)
    # 创建并发服务端对象
    server = socketserver.ThreadingTCPServer(ip_port, MyFtpServer)
    # 开启服务
    server.serve_forever()

1.4.2 服务端进入监听状态后,客户端发起连接请求,服务端接收连接请求后会等待客户单发来状态码,1表示请求登录FTP服务器,2表示客户端要注册用户,注册用户需要服务端手动反馈状态码1才可注册。处理用户登录,注册模块login.py核心代码如下:

class Login(FileOperation):
    """
    登陆注册类。主要负责用户的登陆认证,和用户注册。
    """
    def run_client_choice(self,socket_obj,commom):
        """
        获取客户端的请求,1是登陆,2是注册用户
        :param socket_obj: socket对象
        :param commom: ftpserver运行时所需要的数据对象
        :return:
        """
        recv_choice = socket_obj.recv(1).decode("utf-8")  # 获取用户选项:1是登陆,2是注册用户
        if recv_choice == "1":  # client请求登陆
            return self.login_authen(socket_obj,commom)
        elif recv_choice == "2":  # client请求注册账号
            return self.register_user(socket_obj,commom)
        else:
            return -1  # client断开连接了

    # 用户登陆认证
    def login_authen(self,socket_obj,commom):
        """
        客户端登陆认证
        :param socket_obj:  socket对象
        :param commom:  ftpserver运行时需要的数据对象
        :return:返回1代表登陆成功
        """
        # 接收client发来的用户名,密码
        recv_userPwd = self.recv_data(socket_obj).decode("utf-8").split("|")
        # 效验用户名密码
        check_ret = self.check_user_pwd(recv_userPwd, socket_obj,commom)
        if check_ret:  # 用户名密码正确
            self.check_user_home_dir(commom,recv_userPwd[0]) # 检测用户家目录
            return commom.status_info["login_success"]
        else:
            return commom.status_info["login_fail"]

   ...

    # 注册用户
    def register_user(self,socket_obj,commom):
        """
        :param socket_obj:
        :param commom:
        :return: 返回是否允许注册的结果,1允许客户端注册,2拒绝客户端注册
        """
        while True:
            choice_id = input("请输入回应码:1是允许注册,2是不允许注册:")
            if choice_id.isdigit() and 3 > int(choice_id) > 0:
                socket_obj.send(choice_id.encode("utf-8"))  # 发通知告知客户端,处理结果
                if choice_id == "1":  # 注册用户
                    return self.client_register(socket_obj, commom)
                return choice_id
            else:
                print("您输入的信息有误,请重新输入。")

   ...

1.4.3 客户端登录成功后,服务端会等待接收客户端发来的命令,命令的解析,执行由dataAnalysis.py模块执行,核心代码如下:

class DataAnalysis(FileOperation):
    """
    数据分析处理类,主要负责解析client发送过来的指令。
    """
    def syntax_analysis(self,recv_data, socket_obj, commom):
        """
        负责解析客户端传来的数据。
        :param recv_data:接收到的客户端用户数据
        :param socket_obj:socket对象
        :param commom:数据对象
        :return:
        """
        clientData = recv_data.split(" ")
        if hasattr(self,clientData[0]):  # 判断对象方法是否存在
            get_fun = getattr(self,clientData[0])#获取对象方法
            get_fun(clientData,socket_obj,commom)  # 运行对象方法
        else:
            pass
    ...

执行客户端命令后,继续等待接收客户端发来的命令,如此循环...。

2、客户端

2.1 目录结构如下:

2.2 目录简介:

client:程序主目录。

bin:程序入口,程序启动文件main.py用于建立socket连接,然后调用FTPclient.py模块下的run_ftp_client方法运行程序。

app:程序主逻辑,目录下有四个模块如下:

FTPclient.py:FTP客户端,根据用户选项,执行用户指令。

login.py:认证注册模块,用于处理用户注册,登录认证。

dataAnalysis.py:命令解析模块,解析用户输入的命令,发给服务端获取结果。

FileOpertion.py:负责文件读,写。

lib:存放公共数据,有两个文件:

commom.py:主要存放的是公共变量。

help.txt:存放的是帮助文档,当用户执行help命令时会调用该文件。

2.3 模块中类的继承关系

2.4 执行流程

2.4.1 程序入口main.py,启动后会与FTP服务端建立连接,与服务端连接成功后会调用FTPclient.py模块下的run_ftp_client方法,执行用户功能。核心代码如下:

socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_obj.connect(("192.168.10.10",9000))

client_obj = Client()
client_obj.run_ftp_client(socket_obj)  # 接收用户输入的选项,执行对应的功能

2.4.2 FTPclient.py模块下的run_ftp_client方法会打印菜单,并等待用户输入选项,执行相应功能,核心代码如下:

class Client(Login,DataAnalysis):

    def run_ftp_client(self,socket_obj):
        """
        运行用户输入的选项:1、是登陆  2、是注册账号
        :return:
        """
        while True:
            self.login_menu()  # 打印系统菜单
            choice_id = self.get_user_choice()  # 获取用户输入的选项
            if choice_id:
                if self.run_user_choice(choice_id,socket_obj):
                    break
            else:
                print("您输入的有误")
    def get_user_choice(self):
        """
        获取用户输入的选项
        :return:
        """
        choice_id = input("请输入选项:")
        if choice_id.isdigit() and 4 > int(choice_id) > 0 or choice_id.upper() == "Q":
            return choice_id
        return False
    def run_user_choice(self,choice_id,socket_obj):
        if choice_id == "1":  # 登陆系统
            socket_obj.send(choice_id.encode("utf-8"))  # 发通知告知服务器准备登陆
            if self.run_login(socket_obj) == True:  # 执行登陆
                return True
        elif choice_id == "2":  # 注册用户
            socket_obj.send(choice_id.encode("utf-8"))  # 请求服务器,注册用户
            self.register_user(socket_obj)  # 执行注册
        elif choice_id.upper() == "Q":  # 退出程序
            socket_obj.send(choice_id.encode("utf-8"))  # 通知服务器,准备退出程序
            socket_obj.close()
            print("程序正常退出")
            return True

    def run_login(self,socket_obj,):
        """
        运行登陆认证模块,如果登陆成功执行程序主逻辑,否则重新登陆。
        :param socket_obj:
        :return:
        """
        if self.login_authention(socket_obj):
            while True:
                send_data = input(">>>").strip(" ")  # 获取发送数据(用户执行的命令)
                if send_data.upper() == "Q":  # 正常退出程序
                    socket_obj.send(send_data.encode("utf-8"))  # 通知服务区断开连接
                    socket_obj.close()
                    print("程序正常退出")
                    return True
                if self.syntax_analysis(send_data, socket_obj):  # 解析用户数据并处理数据
                    print("异常退出")
                    return True
        return False

    def login_menu(self):
        print("-"*41)
        print("          欢迎登陆迷你FTPv1.0")
        print("-"*41)
        print("1、登陆系统")
        print("2、用户注册")
        print("Q、退出程序")

2.4.3 login.py模块主要用于处理注册和登录的功能,核心代码如下:

class Login(Commom):
    def login_authention(self,socket_obj):
        """
        登陆认证
        :param socket_obj:socket 对象
        :return:
        """
        user_pwd = self.get_user_pwd()  # 获取用户名密码
        self.send_data(socket_obj,user_pwd)  # 将用户名和密码发给服务器
        recv_status = socket_obj.recv(2).decode("utf-8")  # 等待接收状态码
        print(self.status_info[recv_status])  # 打印状态码对应的结果
        if self.status_info[recv_status] == ‘登录成功‘:
            return True
        return False
    ...

    def register_user(self,socket_obj):
        """
        等待服务端反馈是否允许注册用户。
        :param socket_obj:
        :return:
        """
        print("请等待服务端回应.....")
        recv_status = socket_obj.recv(1).decode("utf-8")
        if recv_status == "1":  # 服务端同意申请账号
            user_pwd = self.get_regist_user_pwd()  # 获取注册用户名和密码
            if user_pwd:
                self.send_data(socket_obj,user_pwd)
                result = socket_obj.recv(2).decode("utf-8")
                print(self.status_info[result])
            else:
                print("用户名密码有误")
        else:  # 客户端拒绝申请账号的请求
            print("服务端拒绝了您申请账号的请求,请与管理员取得联系。")
        return False
    ...

2.4.4 用户登录成功后,会等待接收用户输入命令,由dataAnalysis.py模块负责解析用户输入的命令,并将命令发给FTP服务器,然后接收服务器的反馈。核心代码如下:

class DataAnalysis(FileOperation):

    def syntax_analysis(self,cmd,socket_obj):
        """
        解析用户输入的命令。
        :param cmd:用户执行的命令,如:put 上传的文件
        :param socket_obj:socket对象发送和接收数据
        :return:
        """
        cmd_split = cmd.split(" ")  # 将字符串命令分割成列表,用于验证命令是否存在
        if hasattr(self,cmd_split[0]):
            run_fun = getattr(self,cmd_split[0])
            run_fun(cmd_split,socket_obj)
        else:
            print("无效的命令")
    ...

三、功能演示

原文地址:https://www.cnblogs.com/caesar-id/p/12105321.html

时间: 2024-10-16 08:19:48

python 实现简单的FTP的相关文章

python之路——作业:开发简单的FTP

一.作业要求 开发简单的FTP:1. 用户登陆2. 上传/下载文件3. 不同用户家目录不同4. 查看当前目录下文件5. 充分使用面向对象知识 二.程序目录说明 FTP/ |-- FTPClient/ #客户端文件夹 | |-- 示例文件夹/ #客户端上传/下载示例文件夹 | |-- Client_start.py #客户端启动程序 | |-- FTPServer/ #服务端文件夹 | |-- bin/ | | |-- __init__.py | | |-- Server_start.py #程序

python:使用ftplib编写FTP客户端

Python中的ftplib模块 Python中默认安装的ftplib模块定义了FTP类,其中函数有限,可用来实现简单的ftp客户端,用于上传或下载文件 FTP的工作流程及基本操作可参考协议RFC959 ftp登陆连接 from ftplib import FTP #加载ftp模块 ftp=FTP() #设置变量 ftp.set_debuglevel(2) #打开调试级别2,显示详细信息 ftp.connect("IP","port") #连接的ftp sever和

SELECTORS模块实现并发简单版FTP

环境:windows, python 3.5功能:使用SELECTORS模块实现并发简单版FTP允许多用户并发上传下载文件 结构:ftp_client ---| bin ---| start_client.py ......启动客户端 conf---| config.py ......客户端参数配置 system.ini ......客户端参数配置文件 core---| clients.py ......客户端主程序 home ......默认下载路径ftp_server ---| bin --

Python一秒搭建ftp服务器,帮助你在局域网共享文件【华为云技术分享】

“老板 来碗面” “要啥面?” “内牛满面..” 最近项目上的事情弄得人心累,本来是帮着兄弟项目写套入口代码,搞着搞着就被拉着入坑了.搞开发的都知道,最怕弄这种项目portal的东西,你调用一堆东西,结果各种调用报错都反馈到你这里,导致的结果就是除了啥问题都找你. 最形象的比喻就是,眼前一栋楼,你是看门的.电梯坏了找你.住户被盗了找你.连谁家下水不通了也找你,各种无厘头的破事儿,我就想送出一张图… 共享文件 熟悉Python的朋友们都知道,python自带了一个Simple HTTP Serve

python中简单文件的输入三种方式

最近在自学python,简单的总结了一下文件的输入的方式. 1. f=open("foo.txt") line=f.readline() while line: print(line,end='') line=f.readline() f.close() 2. for line in open("foo.txt"): print(line,end='') 3. f=open("foo.txt") lines=f.readlines() for l

python之模块ftplib(FTP协议的客户端)

# -*- coding: utf-8 -*- #python 27 #xiaodeng #python之模块ftplib(FTP协议的客户端) #需求:快速进行ftp上传 ,下载,查询文件 from ftplib import FTP ftp = FTP() #设置变量 timeout = 30 port = 21 ftp.connect('192.168.1.188',port,timeout) # 连接FTP服务器 ftp.login('UserName','888888') # 登录 p

Python实现简单的猜数字游戏

Python实现简单的猜数字游戏,具体如下: 随机生成一个1-10之间的数字,让用户来猜,当猜错时,会提示猜的数字是大还是小了,直到用户猜对为止. import random secret = random.randint(1,10) #print(secret) print('------猜数字游戏!-----') guess = 0 while guess != secret: temp = input('猜数字游戏开始,请输入数字:') guess = int(temp) if guess

用 python实现简单EXCEL数据统计

任务: 用python时间简单的统计任务-统计男性和女性分别有多少人. 用到的物料:xlrd 它的作用-读取excel表数据 代码: import xlrd workbook = xlrd.open_workbook('demo.xlsx') #打开excel数据表 SheetList = workbook.sheet_names()#读取电子表到列表 SheetName = SheetList[0]#读取第一个电子表的名称 Sheet1 = workbook.sheet_by_index(0)

python实现简单的循环购物车小功能

python实现简单的循环购物车小功能 # -*- coding: utf-8 -*- __author__ = 'hujianli' shopping = [ ("iphone6s", 5000), ("book python", 81), ("iwach", 3200), ("电视机", 2200) ] def zero(name): if len(name) == 0: print("\033[31;1m您的输