FTP

环境:windows Python  3.5
实现功能:
注册/登陆,不同用户家目录不同,查看登陆用户家目录下文件,上传到登陆用户家目录下,从登陆用户家目录下下载
结构:
FTP--|
clients--|
conf--|
config.py-- ……客户端参数,IP,PORT
core--|
client.py-- ……客户端启动文件
servers--|
conf--|
config.py-- ……服务端参数,IP,PORT,上传最大文件大小,家目录路径
core--|
client.py-- ……服务端启动文件
db --|
data.py-- ……存储,读取用户信息
home--| 存放不同用户家目录

如何使用:
启动服务端servers.core.servers.py文件
启动客户端clients.core.client.py文件(可多用户登陆)
未注册先注册,注册后登陆
根据需求选择相应操作:
上传默认路径为登陆用户家目录 home/name
下载默认从登陆用户家目录下下载文件,使用下载前先上传文件,否则将无任何文件可下载

客户端代码:

import os
import configparser
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

class Configuration(object):
    def __init__(self):
        self.config = configparser.ConfigParser()
        self.name = PATH+os.sep+"conf"+os.sep+"system.ini"

    def init_config(self):
        # 初始化配置文件,ip :客户端IP,port:客户端端口
        if not os.path.exists(self.name):
            self.config["config"] = {"ip":"localhost","port":6666}
            self.config.write(open(self.name, "w", encoding="utf-8", ))

    def get_config(self, head="config"):
        '''
        获取配置文件数据
        :param head: 配置文件的section,默认取初始化文件config的数据
        :return:返回head中的所有数据(列表)
        '''
        self.init_config() # 取文件数据之前生成配置文件
        self.config.read(self.name, encoding="utf-8")
        if self.config.has_section(head):
            section=self.config.sections()
            return self.config.items(section[0])
config.py
#!/usr/bin/env python
# -*-coding:utf-8-*-
# _author_=zh
import os
import sys
import socket
import hmac
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
from conf import config


class Client(object):

    def __init__(self, ip, port):
        # 创建连接
        self.ip = str(ip)
        self.port = int(port)
        self.client = socket.socket()
        self.client.connect((self.ip, self.port))

    def send_pwd(self):
        # 注册和登录中公有的操作,返回服务器返回的标识,0为成功,1为失败,返回登录人名称
        logo_name = input("name:")
        pwd = input("pwd:")
        data = "%s" % ([logo_name, self.encrypt("pwd", pwd)])
        self.client.sendall(data.encode())
        accept_data = self.client.recv(1024)
        return accept_data, logo_name

    def login(self):
        # 注册
        self.client.sendall("login".encode())
        while True:
            accept_data, logo_name = self.send_pwd()
            if accept_data.decode() == "0":
                return None
            elif accept_data.decode() == "1":
                print("账号已存在")
                break

    def logon(self):
        # 登录,登录成功返回登录人姓名
        self.client.sendall("logon".encode())
        count = 0
        while True:
            accept_data, logo_name = self.send_pwd()
            if accept_data.decode() == "0":
                return logo_name
            elif accept_data.decode() == "1":
                print("账号或密码错误")
                count += 1
                if count > 2:
                    break

    def watch(self, logo_name):
        # 查看登陆用户家目录下文件
        self.client.sendall("watch".encode())
        data = ["dir", logo_name]
        self.client.sendall(str(data).encode())
        accept_data = self.client.recv(1024)
        accept_data = accept_data.decode()
        print(accept_data)

    def upload(self, logo_name):
        # 上传,上传到home目录下
        self.client.send("upload".encode())
        path = input("请输入需要上传的文件路径:")
        length = os.path.getsize(path)
        file_name = path[path.rfind("\\")+1:]
        data = ("['%s','%s','%s']" % (length, file_name, logo_name)).encode()
        self.client.send(data)
        sign = (self.client.recv(1024)).decode()
        if sign == "1":
            print("家目录空间不足")
        else:
            get_length = (self.client.recv(1024)).decode()
            if get_length == "None":
                print("文件已存在")
            else:
                get_length = int(get_length)
                conf = hmac.new("file".encode())
                with open(path, "rb") as send_data:
                    send_data.seek(get_length)
                    send_data = send_data.read()
                    conf.update(send_data)
                    pwd = conf.hexdigest()
                self.client.sendall(send_data)
                if (self.client.recv(1024)).decode() == "0":
                    self.client.send(pwd.encode())
                if (self.client.recv(1024)).decode() == "0":
                    print("上传成功")
                else:
                    print("上传失败")

    def download(self, logo_name):
        # 下载,从登陆用户家目录下载到指定位置
        self.client.send("download".encode())
        self.client.send(("%s" % logo_name).encode())
        show_data = self.client.recv(1024)
        print(show_data.decode())
        while True:
            file_name = input("请输入需要下载的文件名:")
            if "." not in file_name:
                print("文件名需带格式后缀")
            address = input("请输入下载路径:")
            if os.path.exists(address):
                break
            print("文件路径不存在,重新输入")
        self.client.send(file_name.encode())
        length_data = self.client.recv(1024)
        length_data = eval(length_data.decode())
        if length_data == 1:
            print("文件不存在")
        else:
            length = length_data[0]
            pwd = length_data[1]
            self.client.send("0".encode())
            all_data = b''
            while True:
                if len(all_data) == int(length):
                    break
                else:
                    data = self.client.recv(1024)
                    all_data += data
                    ret = len(all_data) / int(length)
                    num = int(ret * 100)
                    view = '\r [%-100s]%d%%' % ("=" * num, 100,)
                    sys.stdout.write(view)
                    sys.stdout.flush()
            path = address+os.sep+file_name
            if os.path.isfile(path):
                print("文件已存在")
            else:
                conf = hmac.new("file".encode())
                with open(path, "wb") as file:
                    file.write(all_data)
                    conf.update(all_data)
                    new_pwd = conf.hexdigest()
                if new_pwd == pwd:
                    print("下载成功")
                else:
                    print("下载失败")
                    os.remove(path)

    def encrypt(self, keys, data):
        #  hmac加密
        conf = hmac.new(keys.encode())
        conf.update(data.encode())
        return conf.hexdigest()


if __name__ == '__main__':
    while True:
        info = config.Configuration()
        info_list = info.get_config()
        obj_client = Client(info_list[0][1], info_list[1][1])
        info = '''---welcome---
        1.登录
        2.注册
        '''
        print(info)
        num = input("-->")
        setattr(obj_client, "1", obj_client.logon)
        setattr(obj_client, "2", obj_client.login)
        if hasattr(obj_client, num):
            name = getattr(obj_client, num)()
        else:
            print("输入错误,请重新输入")
            continue
        if name:
            break
        else:
            print("请登录")
    while True:
        info = '''
    1.查看家目录下文件
    2.上传
    3.下载
    4.注销
    '''
        print(info)
        num = input("-->")
        if num == "4":
            exit()
        setattr(obj_client, "1", obj_client.watch)
        setattr(obj_client, "2", obj_client.upload)
        setattr(obj_client, "3", obj_client.download)
        if hasattr(obj_client, num):
            getattr(obj_client, num)(name)
        else:
            print("输入错误,请重新输入")
            continue
client

服务端代码:

import os
import configparser
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


class Configuration(object):
    def __init__(self):
        self.config = configparser.ConfigParser()
        self.name = PATH+os.sep+"conf"+os.sep+"system.ini"

    def init_config(self):
        # 初始化配置文件,IP:服务端IP,port:服务端端口,limit_size:家目录容量,HOME_PATH:用户家目录地址
        if not os.path.exists(self.name):
            self.config["config"] = {"ip": "localhost", "port": 6666, "limit_size": 10240000,
                                     "HOME_PATH": PATH+os.sep+"home"+os.sep}
            self.config.write(open(self.name, "w", encoding="utf-8", ))

    def get_config(self, head="config"):
        '''
        获取配置文件数据
        :param head: 配置文件的section,默认取初始化文件config的数据
        :return:返回head中的所有数据(列表)
        '''
        self.init_config()  # 取文件数据之前生成配置文件
        self.config.read(self.name, encoding="utf-8")
        if self.config.has_section(head):
            section = self.config.sections()
            return self.config.items(section[0])
config.py
import shelve
import os
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+os.sep+"db"+os.sep


def write(name, data):
    '''
    存储个人信息
    :param data: 存储的数据
    :param name: shelve的key
    '''
    file = shelve.open("%sdata" % PATH)
    file[name] = data
    file.close()


def read(name):
    '''
    读取个人信息
    :param name:存储时传入的name
    :return:返回个人信息
    '''
    file = shelve.open("%sdata" % PATH)
    if name in list(file.keys()):
        data = file[name]
    else:
        data = None
    file.close()
    return data
data.py
#!/usr/bin/env python
# _*_coding:utf-8_*_
# _author_=zh
import os
import locale
import codecs
import subprocess
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+os.sep+"home"


def order(cmd):
    '''
    执行命令结果输出到屏幕
    :param cmd: 输入的命令
    :return:
    '''
    word = subprocess.Popen(args=cmd, cwd=PATH, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    # 将结果decode输出,自动获取不同操作系统的默认编码
    return word.stderr.read().decode(codecs.lookup(locale.getpreferredencoding()).name), \
        word.stdout.read().decode(codecs.lookup(locale.getpreferredencoding()).name) 
cmd.py
import os
import sys
import socketserver
import cmd
import hmac

PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH)
import db
from conf import config


class Myserver(socketserver.BaseRequestHandler):

    def handle(self):
        self.conn = self.request
        self.addr = self.client_address
        print("一个连接已进入")
        try:
            while True:
                # 客户端传入对应的方法名称,调用服务端相同的方法名
                accept_data = self.conn.recv(1024)
                accept_data = accept_data.decode("utf-8")
                if accept_data in list(Myserver.__dict__.keys()):
                    func = 'self.%s' % accept_data
                    eval(func)()
                else:
                    print("服务端%s方法缺失" % accept_data)
                    break
        except (ConnectionResetError, ConnectionAbortedError, SyntaxError):
            print("连接关闭")
        self.conn.close()

    def logon(self):
        # 登录,登录成功自动创建家目录,目录名为登录名
        while True:
            accept_data = self.conn.recv(1024)
            accept_data = eval(accept_data.decode("utf-8"))
            list_data = db.data.read(accept_data[0])
            if not list_data:
                self.conn.send("1".encode())
            else:
                if accept_data == list_data:
                    cmd.order("mkdir %s" % accept_data[0])
                    self.conn.send("0".encode())
                    break

    def login(self):
        # 注册
        accept_data = self.conn.recv(1024)
        accept_data = eval(accept_data.decode("utf-8"))
        list_data = db.data.read(accept_data[0])
        if list_data:
            self.conn.send("1".encode())
        else:
            db.data.write(accept_data[0], accept_data)
            self.conn.send("0".encode())

    def watch(self):
        # 查看登陆用户家目录下文件信息并返回给客户端
        accept_data = self.conn.recv(1024)
        accept_data = eval(accept_data.decode("utf-8"))
        send_data = "%s %s%s" % (accept_data[0], info_list[3][1], accept_data[1])
        eer, out = cmd.order(send_data)
        self.conn.send(("%s,%s" % (eer, out)).encode())

    def upload(self):
        # 上传到登陆用户的家目录
        accept_data = self.conn.recv(1024)
        accept_data = accept_data.decode("utf-8")
        accept_data = eval(accept_data)
        # accept_data = [length, file_name, logo_name]
        if int(info_list[2][1]) < int(accept_data[0]):
            self.conn.send("1".encode())
        else:
            self.conn.send("0".encode())
            path = info_list[3][1]+accept_data[2]+os.sep+accept_data[1]
            accept_data[0] = int(accept_data[0])
            try:
                length = os.path.getsize(path)
            except FileNotFoundError:
                length = 0
            if accept_data[0] == length:
                self.conn.send("None".encode())
            else:
                accept_data[0] = accept_data[0]-length
                self.conn.sendall(("%s" % length).encode())
                get_bytes = b''
                while True:
                    get_data = self.conn.recv(1024)
                    if len(get_data) == accept_data[0]:
                        get_bytes = get_data
                        break
                    else:
                        get_bytes += get_data
                        if len(get_bytes) == accept_data[0]:
                            self.conn.send("0".encode())
                            break
                if length == 0:
                    with open(path, "wb") as file:
                        file.write(get_bytes)
                else:
                    with open(path, "ab") as file:
                        file.write(get_bytes)
                conf = hmac.new("file".encode())
                with open(path, "rb") as file:
                    data = file.read()
                    conf.update(data)
                    pwd = conf.hexdigest()
                get_pwd = (self.conn.recv(1024)).decode()
                if pwd == get_pwd:
                    self.conn.send("0".encode())
                else:
                    self.conn.send("1".encode())
                    os.remove(path)

    def download(self):
        # 从登陆用户家目录下载文件,到指定目录
        logo_name = (self.conn.recv(1024)).decode()
        file_path = info_list[3][1]+os.sep+logo_name
        send_data = "dir %s" % file_path
        eer, out = cmd.order(send_data)
        self.conn.send(("%s,%s" % (eer, out)).encode())
        file_name = (self.conn.recv(1024)).decode()
        conf = hmac.new("file".encode())
        if os.path.isfile(file_path+os.sep+file_name):
            with open(file_path+os.sep+file_name, "rb") as send_data:
                send_data = send_data.read()
                conf.update(send_data)
                pwd = conf.hexdigest()
            self.conn.send(("['%s','%s']" % (len(send_data), pwd)).encode())
            sign = (self.conn.recv(1024)).decode()
            if sign == "0":
                self.conn.sendall(send_data)
        else:
            self.conn.send("1".encode())


if __name__ == '__main__':
    info = config.Configuration()
    info_list = info.get_config()
    server = socketserver.ThreadingTCPServer((info_list[0][1], int(info_list[1][1])), Myserver)
    server.serve_forever() 
servers.py

 

posted @ 2017-12-12 17:03  zhuh  阅读(173)  评论(0编辑  收藏  举报