• 项目


    程序结构说明

    # 文件上传-断点续传
    
    文件上传时可能会异常终止,因此只下载了一部分,所以我们可以重新连接之后接着下载.
    
    # 目录结构
    
    |- client.py
    |- server.py
    |- conf
        |- settings.py
    |- lib
        |- common.py
    |- log
        |- log.log
    |- db
        |- server_db
        |- client_db
    |- README.md
    |- requirement.txt
    
    # client.py
    
    发送指令给服务端,下载文件.
    
    # server.py
    
    实现并发的服务端,提供文件.
    
    ## 文件的多种状态
    
    1. 全新的文件,未下载的文件
    2. 下载一部分的文件
    3. 下载完成的文件
    
    # conf/settings.py
    
    IP = ''
    PORT = ''
    BASE_PATH = ''
    DB_PATH = ''
    CLIENT_DB_PATH = ''
    SERVER_DB_PATH = ''
    LOG_PATH = ''
    
    日志的模板
    
    # lib/common.py
    
    通用模板,如:日志.
    
    # log/log.log
    
    记录日志,**按天新增文件**
    
    # db
    
    存储客户端/服务端数据
    
    
    1. ls
    

    conf 配置文件目录

    ####################
    # log_settings.py  #
    ####################
    
    ## 日志模块配置
    import os
    
    # 定义三种日志输出格式 开始
    standard_format = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]' 
                      '[%(levelname)s][%(message)s]'  # 其中name为getLogger()指定的名字;lineno为调用日志输出函数的语句所在的代码行
    simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
    id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'
    # 定义日志输出格式 结束
    
    logfile_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # log文件的目录,需要自定义文件路径 # atm
    logfile_dir = os.path.join(logfile_dir, 'log')  # C:UsersoldboyDesktopatmlog
    
    logfile_name = '{name}.log'  # log文件名,需要自定义路径名
    
    # 如果不存在定义的日志目录就创建一个
    if not os.path.isdir(logfile_dir):  # C:UsersoldboyDesktopatmlog
        os.mkdir(logfile_dir)
    
    # log文件的全路径
    logfile_path = os.path.join(logfile_dir, logfile_name)  # C:UsersoldboyDesktopatmloglog.log
    # 定义日志路径 结束
    
    # log配置字典
    LOGGING_DIC = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'standard': {
                'format': standard_format
            },
            'simple': {
                'format': simple_format
            },
        },
        'filters': {},  # filter可以不定义
        'handlers': {
            # 打印到终端的日志
            'console': {
                'level': 'DEBUG',
                'class': 'logging.StreamHandler',  # 打印到屏幕
                'formatter': 'simple'
            },
            # 打印到文件的日志,收集info及以上的日志
            'default': {
                'level': 'INFO',
                'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件
                'formatter': 'standard',
                'filename': logfile_path,  # 日志文件
                'maxBytes': 1024 * 1024 * 5,  # 日志大小 5M  (*****)
                'backupCount': 5,
                'encoding': 'utf-8',  # 日志文件的编码,再也不用担心中文log乱码了
            },
        },
        'loggers': {
            # logging.getLogger(__name__)拿到的logger配置。如果''设置为固定值logger1,则下次导入必须设置成logging.getLogger('logger1')
            '': {
                # 这里把上面定义的两个handler都加上,即log数据既写入文件又打印到屏幕
                'handlers': ['default', 'console'],
                'level': 'DEBUG',
                'propagate': False,  # 向上(更高level的logger)传递
            },
        },
    }
    
    
    ################
    # setttings.py #
    ################
    import os
    IP = '192.168.11.210'
    PORT = 8000
    BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    DB_PATH = os.path.join(BASE_PATH, 'db')
    CLIENT_DB_PATH = os.path.join(DB_PATH, 'client')
    SERVER_DB_PATH = os.path.join(DB_PATH, 'server')
    LOG_PATH = os.path.join(BASE_PATH, 'log')
    
    
    

    db 数据库文件夹

    client

    server

    lib 文件夹

    ##############
    #  common.py #
    ##############
    
    
    import os
    import logging
    import logging.config
    from conf import log_settings,settings
    
    def load_my_logging_cfg(name):
    
        logfile_name = f'{name}.log'
        logfile_path = os.path.join(settings.LOG_PATH, logfile_name)
        log_settings.LOGGING_DIC['handlers']['default']['filename'] = logfile_path
    
        logging.config.dictConfig(log_settings.LOGGING_DIC)  # 导入上面定义的logging配置
        logger = logging.getLogger(name)  # 生成一个log实例
        logger.info(f'{name} works!')  # 记录该文件的运行状态
    
        return logger
    
    if __name__ == '__main__':
        load_my_logging_cfg('aaa')
    
    

    log 文件夹

    作用:记录日志信息

    clent 客户端主程序

    #############
    # client.py #
    #############
    import os
    import struct
    from socket import *
    from conf import settings
    from lib import common
    
    logger = common.load_my_logging_cfg('client')
    
    client = socket()
    client.connect(('127.0.0.1', 8000))
    
    def save_file_content(filename, file_content):
        """保存文件内容"""
        file_path = os.path.join(settings.CLIENT_DB_PATH, filename)
        with open(file_path, 'ab') as fw:
            fw.write(file_content)
    
    while True:
    
        # 接收文件列表
    
        client.send('ls'.encode('utf8'))
        file_list = client.recv(1024)
        file_list = eval(file_list.decode('utf8'))
        for ind, val in enumerate(file_list):
            print(ind, val)
    
        # 选择文件
        file_choice = input('请选择你需要下载的文件:')  # 0
        filename = file_list[int(file_choice)]  # type:str
    
        # 发送文件和文件大小
        file_path = os.path.join(settings.CLIENT_DB_PATH, filename)
    
        if os.path.exists(file_path):
            file_size = os.path.getsize(file_path)
            client.send(str((filename, file_size)).encode('utf8'))
        else:
            client.send(str((filename, 0)).encode('utf8'))
    
        # 接收文件并保存
        # 接收文件头
        file_head = client.recv(4)
        file_head = struct.unpack('i', file_head)[0]  # file_size
        print(file_head)
        # 接收文件内容
        # print(file_head)
        recv_size = 0
        if file_head!=0:
            while recv_size <= file_head:
                file_content = client.recv(10240)
                recv_size += 10240
                print('recv_size:', recv_size)
                save_file_content(filename, file_content)
            else:
                print('啥玩意')
        else:
            print('文本已经存在')
        logger.info(f'{filename}下载成功')
    

    server 服务端主程序

    ##############
    #  server.py #
    ##############
    
    import os
    import struct
    from socket import *
    from lib import common
    from conf import settings
    
    logger = common.load_my_logging_cfg('server')
    
    server = socket()
    server.bind((settings.IP, settings.PORT))
    server.listen(5)
    
    
    def get_filename_list():
        """获取db/server下的所有文件名"""
        filename_list = os.listdir(settings.SERVER_DB_PATH)
    
        return filename_list
    
    
    def get_file_content(filename, file_size):
        """通过文件名获取文件路径"""
        filename_path = os.path.join(settings.SERVER_DB_PATH, filename)
        with open(filename_path, 'rb') as fr:
            fr.seek(file_size, 0)
            file_content = fr.read(1024)
    
        return file_content
    
    
    def get_file_content_iter(filename, file_size):
        """通过文件名获取文件路径"""
        filename_path = os.path.join(settings.SERVER_DB_PATH, filename)
        with open(filename_path, 'rb') as fr:
            fr.seek(file_size, 0)
            while True:
                file_content = fr.read(10240)
                print(file_content)
                if file_content:
                    yield file_content
                else:
                    break
    
    
    def set_file_head(filename, file_size):
        """设计一个文件头"""
        filename_path = os.path.join(settings.SERVER_DB_PATH, filename)
        file_head = os.path.getsize(filename_path) - file_size
        file_head = struct.pack('i', file_head)
    
        return file_head
    
    
    print('start...')
    while True:
        conn, client_addr = server.accept()
        print(client_addr)
        while True:
            try:
                # 发送文件列表
                data = conn.recv(1024)
                if data.decode('utf8') == 'ls':
                    filename_list = get_filename_list()
                    conn.send(str(filename_list).encode('utf8'))
                    logger.info(f'{client_addr}查看文件列表')
    
                # 收到文件名
                filename, file_size = eval(conn.recv(1024).decode('utf8'))
                logger.info(f'{client_addr}下载文件{filename}')
                file_content = get_file_content(filename, file_size)
    
                # 发送文件
                file_head = set_file_head(filename, file_size)
                # 发送文件头
                conn.send(file_head)
    
                # 发送文件内容
                for file in get_file_content_iter(filename, file_size):
                    if file:
                        conn.send(file)
                    else:
                        break
    
                logger.info(f'成功给{client_addr}发送文件{filename}')
    
            except ConnectionResetError:
                break
        conn.close()
    
    
    

    server 服务端支持并发

    ##########################
    # socketserver_server.py #
    ##########################
    
    import socketserver
    import os
    import struct
    from lib import common
    from conf import settings
    
    logger = common.load_my_logging_cfg('server')
    
    
    class MyHandler(socketserver.BaseRequestHandler):
        @staticmethod
        def get_filename_list():
            """获取db/server下的所有文件名"""
            filename_list = os.listdir(settings.SERVER_DB_PATH)
    
            return filename_list
    
        @staticmethod
        def get_file_content(filename, file_size):
            """通过文件名获取文件路径"""
            filename_path = os.path.join(settings.SERVER_DB_PATH, filename)
            with open(filename_path, 'rb') as fr:
                fr.seek(file_size, 0)
                file_content = fr.read(1024)
    
            return file_content
    
        @staticmethod
        def get_file_content_iter(filename, file_size):
            """通过文件名获取文件路径"""
            filename_path = os.path.join(settings.SERVER_DB_PATH, filename)
            with open(filename_path, 'rb') as fr:
                fr.seek(file_size, 0)
                while True:
                    file_content = fr.read(10240)
                    yield file_content
    
        @staticmethod
        def set_file_head(filename, file_size):
            """设计一个文件头"""
            filename_path = os.path.join(settings.SERVER_DB_PATH, filename)
            file_head = os.path.getsize(filename_path) - file_size
            file_head = struct.pack('i', file_head)
    
            return file_head
    
        def handle(self):
            print(f'{self.client_address}成功连接')
            logger.info(f'{self.client_address}成功连接')
            print(self.client_address)
            while True:
                try:
                    # 发送文件列表
                    data = self.request.recv(1024)
                    if data.decode('utf8') == 'ls':
                        filename_list = self.get_filename_list()
                        self.request.send(str(filename_list).encode('utf8'))
                        logger.info(f'{self.client_address}查看文件列表')
    
                    # 收到文件名
                    filename, file_size = eval(self.request.recv(1024).decode('utf8'))
                    logger.info(f'{self.client_address}下载文件{filename}')
                    file_content = self.get_file_content(filename, file_size)
    
                    # 发送文件
                    file_head = self.set_file_head(filename, file_size)
                    # 发送文件头
                    self.request.send(file_head)
    
                    # 发送文件内容
                    for file in self.get_file_content_iter(filename, file_size):
                        self.request.send(file)
                    logger.info(f'成功给{self.client_address}发送文件{filename}')
    
                except ConnectionResetError:
                    break
            self.request.close()
    
    
    if __name__ == '__main__':
        server = socketserver.ThreadingTCPServer(('192.168.11.210', 8000), MyHandler, bind_and_activate=True)
        print('start...')
        server.serve_forever()
    
    
  • 相关阅读:
    《杀死比尔 卷1》(Kill Bill: Volume 1)思路.未剪切全彩版.720re.DTS[HDTVRE]
    IronPython 1.0 Release Candidate 2
    Microsoft® Visual Studio® .NET™ 2003 Service Pack 1
    Vista Transformation Pack 5.0 发布
    捡到一小猫
    《魔比斯环》(Thru The Moebius Strip)[DVDScr]
    用户控件 分类列表导航栏
    MsChart<5> 累计柱状图 分类统计
    二进制流上传图片,预览,读取显示
    Ajax无刷新数据绑定
  • 原文地址:https://www.cnblogs.com/bladecheng/p/11102975.html
Copyright © 2020-2023  润新知