• python -服务器与客户端断电续传程序详细介绍


    6.28自我总结

    客户端与服务器之间文件传输断电续传

    `通过判断文件大小,以及文件在读取中的指针位置的调整来解决断电续传问题'

    1.程序目录

    E:/断电续传
    |___bil
    |    |___common.py         #公共方法
    |___conf
    |    |___logger_setting.py  #存日志的格式
    |    |___setting.py         #一些常量
    |___db
    |    |___client      #程序端存文件的文件夹
    |    |___logger      #存日志的文件夹
    |    |___sever  	 #服务端存文件的文件夹	
    |___requirements.txt #方便别人导入库
    |___test.py          #程序写的时候有些东西需要单独拿出来试试
    |___客户端.py  
    |___服务端.py
    

    2.程序思路

    • 第一步:我们什么都不懂,但是有几个模块肯定会有一个是conf中的setting来存一些常量

    • 第二步:我们也需要一个db文件来存内容

    • 第三步:我们要也有个bil存一些可能会出现的公共方法,可以精简代码

    • 第四部:生成requirements.txt文件,方便其他人用模板时候可以一键安装需要的模板

      ​ requirements的自动生成方式cmd中输入pip freeze > requirements.txt

      ​ 然后清空下内容,如果有用到第三方库什么的可以写进去方便别人安装

    • 第五步:根据功能然后进行添加,有些常量丢setting里面,公共方法丢common里面去

    3.程序内容

    1.conf

    1.setting.py

    import os
    
    #项目文件夹路径
    PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    #文件存放夹路径
    DB_PATH = os.path.join(PATH,'db')
    #客户端存放路径
    CLIENT_PATH = os.path.join(PATH,'db','client')
    #服务端存放路径
    SEVER_PATH = os.path.join(PATH,'db','sever')
    #日志存放路径
    LOGGER_PATH = os.path.join(PATH,'db','logger')
    
    #服务端PROT
    IP = '127.0.0.1'
    #服务端端口
    PROT = 8000
    

    2.logger_setting.py

    import os
    import logging.config
    #函数上面部分要根据你程序进行修改
    # 定义三种日志输出格式 开始
    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'
    # 定义日志输出格式 结束
    
    # 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': 1,  # 日志文件
                '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)传递
            },
        },
    }
    

    2.bil

    common.py

    from conf.setting import *
    import struct
    from conf.logger_setting import *
    import logging.config
    import datetime
    
    
    
    #获取文件路径下的子文件的目录列表
    def get_flie_lis(path):
        '''获取文件路径下的子文件的目录列表'''
    
        if os.path.exists(path):return os.listdir(path)
        return False
    
    #获取客文件的大小
    def get_flie_size(filename,filefrom):
        '''获取客文件的大小'''
        path = os.path.join(DB_PATH,filefrom,filename)
    
        if os.path.exists(path): return os.path.getsize(path)
        return 0
    
    #内容大小进行压缩,int格式内容进行压缩成4个字符
    def zip_data(data:int):
        '''内容大小进行压缩,int格式内容进行压缩成4个字符'''
        return struct.pack('i',data)
    
    #解压缩
    def uncompress(zipflie):
        '''解压缩压缩文件'''
        return struct.unpack('i',zipflie)[0]
    
    #根据文件路径读取文件大小以及文件的内容变成个生成器
    def get_file_size_data(path):
        '''根据文件路径读取文件大小以及文件的内容变成个生成器'''
        #生成文件大小
        file_name = path.split('\')[-1]
        file_size = get_flie_size(file_name,'client')
    
        size = os.path.getsize(path)
        with open(path,'rb') as fr:
            fr.seek(file_size,0)
            while True:
                data = fr.read(10240)
                if data == bytes():return True
                yield size,data
    
    #日志
    def load_my_logging_cfg(name):
        '''生成随着时间生成文件夹日志'''
        #生成日志保存路径
        time = str(datetime.datetime.now())
        time = time.split(' ')[0]
        path = os.path.join(LOGGER_PATH,time)
        if not os.path.exists(path):os.mkdir(path)
        file_path = os.path.join(path, f'{name}.log')
    
        #修改定义格式中的地址
        logger_fromat = LOGGING_DIC
        logger_fromat['handlers']['default']['filename'] =file_path
    
        logging.config.dictConfig(logger_fromat)  # 导入上面定义的logging配置
        logger = logging.getLogger(name)  # 生成一个log实例
        logger.info(f'{name} works!')  # 记录该文件的运行状态可以自己修改
    

    3.db

    可以先新建好这三个文件夹,也可以用os.path.exists(path)判断路径来生成

    4.服务端.py

    from socket import *
    from conf.setting import IP,PROT,SEVER_PATH
    from bil.common import *
    from conf.setting import *
    
    
    load_my_logging_cfg('sever')
    #生成服务器
    sever = socket()
    sever.bind((IP,PROT))
    sever.listen(5)
    
    print('start....')
    #消息的传输
    while True:
        #建立连接
        conn,addr = sever.accept()
    
        while True:
            try:
                #进行文件的传输
                #接受1
                request = conn.recv(1024)
                #获得客户端目录下的所有文件名称,并且转换成list
                flie_list = get_flie_lis(SEVER_PATH)
    
                #列表内容进行压缩
                flie_list_str_len = zip_data(len(str(flie_list)))
    
                #发送列表字符串格式大小
                conn.send(flie_list_str_len)
                # 发送列表的字符串
                conn.send(str(flie_list).encode('utf8'))
    
                #接收文件名
                file_name = conn.recv(1024).decode('utf8')
    
                #拼接文件路径
                file_path = os.path.join(SEVER_PATH,file_name)
    
                #判断文件是不是客户端已经下载完成了
                file_name = file_path.split('\')[-1]
                #为了防止客户端因为没有文件而报错,且没有文件把他大小设置成0
                try:
                    file_size = get_flie_size(file_name, 'client')
                except Exception:
                    file_size = 0
                sever_file_size = get_flie_size(file_name, 'sever')
    
                print(file_size,sever_file_size,file_size == sever_file_size)
                # 生成文件内容与文件大小的生成器
                if file_size != sever_file_size:
                    count = 0
                    for size,file_data in get_file_size_data(file_path):
                        #就第一次发送文件大小
                        if count == 0:
    
                            conn.send(zip_data(size))
                            print(str(zip_data(size)).encode('utf8'))
                        print(file_data)
                        conn.send(file_data)
                        count += 1
                else:
                    print(1)
                    #文件已经传输好了客户端有这个文件了
                    conn.send(zip_data(0))
            except:
                break
        conn.close()
    

    5.客户端.py

    from socket import *
    from bil.common import *
    from conf.setting import *
    
    
    load_my_logging_cfg('client')
    #生成客户端
    client = socket()
    
    #与服务端建立连接
    client.connect(('127.0.0.1',8000))
    
    #消息的传输
    while True:
        #自动发送请求获取
        client.send(b'1')
    
        #接受文件目录大小
        list_st_size = client.recv(4)
        #根据文件目录大小获取列表的字符串内容
        data = client.recv(uncompress(list_st_size))
        #将文件目录进行打印
        file_list = eval(data.decode('utf8'))
        print('请选择你要打印文件')
    
        for file_index,file_name in enumerate(file_list):
            print(f'文件编号{file_index}文件名{file_name}')
            
        while True:
            chiose = input('请选择你要下载文件的编号:')
            try:
                chiose_file_name = file_list[int(chiose)]
                break
            except Exception:
                print('序号不存在')
                continue
        
        #发送选择的文件名
        chiose_file_name = file_list[int(chiose)]
        print(chiose_file_name)
        client.send(str(chiose_file_name).encode('utf8'))
    
        #接收文件大小
    
        file_size = uncompress(client.recv(4))
        print(file_size)
    
        if file_size != 0:
            #初始下载文件大小
            client_file_size = get_flie_size(chiose_file_name,'clien')
    
            #下载文件
            path  = os.path.join(CLIENT_PATH,chiose_file_name)
            print(client_file_size)
            print(file_size)
            while client_file_size <file_size:
    
                data = client.recv(10240)
                client_file_size += 10240
                with open(path,'ab') as fa:
                   fa.write(data)
    
                #打印进度条
                if client_file_size < file_size:
                    schedule = int((client_file_size/file_size)*100)
                    #为了好看加了颜色进去
                    print("
    ",f'[33[1;31;40m%-{schedule}s%-3s33[0m%-{100-schedule}s' % ('', f'{schedule}%', ''),end=']')
    
            print("
    ",f'[33[1;31;40m%-100s%-3s33[0m%-0s' % ('', '100%', ''),end='')
            print('')
        else:
            print('')
            print('文件已经下载好了')
        chiose_end = input('输入Q退出下载,输入其他继续下载')
        if chiose_end =='Q' :break
    

    有不足的地方请指出来,谢谢,这是我在写程序中的总结,这是我学习python刚刚好第二个月后的写的程序

  • 相关阅读:
    将各种简单算法组合,使自己更加灵活的使用它
    转载 原反补码
    迭代器是神马东西
    进程 线程
    STL中的容器是如何实现的,如何存储的
    可以实例化对象,但是不能被继承的类
    float的存储及和int的转化
    对批处理、多道操作系统的理解
    STL set、map实现为什么要以红黑树为底层实现机制?
    c 多线程
  • 原文地址:https://www.cnblogs.com/pythonywy/p/11104820.html
Copyright © 2020-2023  润新知