博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
第五十六节,python实现支持并发、断点续传的Ftp程序
阅读量:6125 次
发布时间:2019-06-21

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

一、要求

1、用户md5认证

2、支持多用户同时登陆(并发)

3、进入用户的命令行模式,支持cd切换目录,ls查看目录子文件

4、执行命令(ipconfig)

5、传输文件:

    a、支持断点续传

    b、传输中显示进度条

二、思路

 

1.客户端用户登录和注册:

a、客户端仅提供用户名和密码,选择登录或注册,

b、服务器端进行注册并将加密后的密码写入文件,最后返回给客户端是否登录或注册成功

2.ls和cd命令

a、客户端输入命令,服务器端处理并返回给客户端

3.执行命令:

a、客户端发送需要执行的命令

b、服务器端执行命令,并返回客户端需要接收该命令的次数s=r[0]+1,其中r=divmod(结果总长度,1024)
c、客户端收到次数,告诉服务端已经收到
d、服务端发送执行结果,客户端进行for循环接收该结果

4.发送文件:

a、客户端输入文件路径(测试版路径为:f.png),发送文件名和文件大小

b、服务器端检测指定目录是否含有该文件,如果没有,返回给客户端字符串s,即从头开始发送start,has_recv=0
如果有,即需要断点续传,返回给客户端已经上传了多少has_recv
c、客户端接收返回值,并seek到has_recv的位置,进行循环收发,打印当前进度,直到传输完毕。

注:本程序可循环接收用户选择传输文件和执行命令

三、代码

 

配置文件:

#!/usr/bin/env python# -*- coding: utf-8 -*-import osBASE_DIR = os.path.dirname(os.path.dirname(__file__))  #配置文件的上层目录NEW_FILENAME=os.path.join(BASE_DIR,'view')             #新文件目录NAME_PWD=os.path.join(BASE_DIR,'db','name_pwd')        #用户名和密码目录USER_FILE=os.path.join(BASE_DIR,'db')

服务器端:

#!/usr/bin/env python# -*- coding: utf-8 -*- import sys,osimport timeimport socketimport hashlibimport pickleimport subprocessimport socketserversys.path.append(os.path.dirname(os.path.dirname(__file__)))from config import settings  new=settings.NEW_FILENAMEclass Myserver(socketserver.BaseRequestHandler):     def recv_file(self):        '''        文件传输        :return:        '''        conn=self.request        a=str(conn.recv(1024),encoding='utf-8')        file_size,file_name=a.split(',')        new_file_name=os.path.join(new,file_name)        if file_name in new:            #检测文件是否已存在,涉及断点续传            has_recv=os.stat(new).st_size #计算临时文件大小            conn.sendall(bytes(has_recv,encoding='utf-8'))            with open(new_file_name,'ab') as f:  #追加模式                while has_recv<=int(file_size):                    data=conn.recv(1024)                    f.write(data)                    has_recv+=len(data)        else:            has_recv=0            conn.sendall(bytes('s',encoding='utf-8')) # 客户端收到字符串s,从0开始发送            with open(new_file_name,'wb') as f:                while has_recv<=int(file_size):                    data=conn.recv(1024)                    f.write(data)                    has_recv+=len(data)     def command(self):        '''        执行命令        :return:        '''        conn=self.request        a=conn.recv(1024)        ret=str(a,encoding='utf-8')        ret2 = subprocess.check_output(ret, shell=True)        r=divmod(len(ret2),1024)        s=r[0]+1         #客户端需要接收的次数        conn.sendall(bytes(str(s),encoding='utf-8'))        conn.recv(1024)  #确认客户端收到需要接收的次数         conn.sendall(ret2)     def md5(self,pwd):        '''        对密码进行加密        :param pwd: 密码        :return:        '''        hash=hashlib.md5(bytes('xx7',encoding='utf-8'))        hash.update(bytes(pwd,encoding='utf-8'))        return hash.hexdigest()      def login(self,usrname,pwd):        '''        登陆        :param usrname: 用户名        :param pwd: 密码        :return:是否登陆成功        '''        conn=self.request        s=pickle.load(open(settings.NAME_PWD,'rb'))        if usrname in s:             if s[usrname]==self.md5(pwd):        #和加密后的密码进行比较                return True             else:                return False        else:            return False      def regist(self,usrname,pwd):        '''        注册        :param usrname: 用户名        :param pwd: 密码        :return:是否注册成功        '''         conn=self.request        s=pickle.load(open(settings.NAME_PWD,'rb'))        if usrname in s:             return False        else:            s[usrname]=self.md5(pwd)            mulu=os.path.join(settings.USER_FILE,usrname)            os.makedirs(mulu,'a')            pickle.dump(s,open(settings.NAME_PWD,'wb'))            return True     def before(self,usrname,pwd,ret):        '''        判断注册和登陆,并展示用户的详细目录信息,支持cd和ls命令        :return:        '''        conn=self.request        if ret=='1':            r=self.login(usrname,pwd)            if r:                conn.sendall(bytes('y',encoding='utf-8'))            else:                conn.sendall(bytes('n',encoding='utf-8'))        elif ret=='2':            # print(usrname,pwd)            r=self.regist(usrname,pwd)            if r:                conn.sendall(bytes('y',encoding='utf-8'))            else:                conn.sendall(bytes('n',encoding='utf-8'))    def usr_file(self,usrname):        '''        展示用户的详细目录信息,支持cd和ls命令        :param usrname: 用户名        :return:        '''        conn=self.request        conn.recv(1024)        mulu=os.path.join(settings.USER_FILE,usrname)        conn.sendall(bytes(mulu,encoding='utf-8'))        while True:            b=conn.recv(1024)            ret=str(b,encoding='utf-8')            try:                a,b=ret.split(' ',1)            except Exception as e:                a=ret            if a=='cd':                if b=='..':                    mulu=os.path.dirname(mulu)                else:                    mulu=os.path.join(mulu,b)                conn.sendall(bytes(mulu,encoding='utf-8'))            elif a=='ls':                ls=os.listdir(mulu)                print(ls)                a=','.join(ls)                conn.sendall(bytes(a,encoding='utf-8'))            elif a=='q':                break      def handle(self):        conn=self.request        conn.sendall(bytes('welcome',encoding='utf-8'))        b=conn.recv(1024)        ret=str(b,encoding='utf-8')        print(ret)        conn.sendall(bytes('b ok',encoding='utf-8'))        c=conn.recv(1024)        r=str(c,encoding='utf-8')        usrname,pwd=r.split(',')        self.before(usrname,pwd,ret) #登陆或注册验证        self.usr_file(usrname)  #展示用户的详细目录信息,支持cd和ls命令        while True:            a=conn.recv(1024)            conn.sendall(bytes('收到a',encoding='utf-8'))            ret=str(a,encoding='utf-8')            if ret=='1':                self.recv_file()                # conn.sendall(bytes('file ok',encoding='utf-8'))            elif ret=='2':                self.command()            elif ret=='q':                break            else:                pass if __name__=='__main__':    sever=socketserver.ThreadingTCPServer(('127.0.0.1',9999),Myserver)    sever.serve_forever()

客户端:

#!/usr/bin/env python# -*- coding: utf-8 -*- import sysimport timeimport osimport socketsys.path.append(os.path.dirname(os.path.dirname(__file__)))from config import settings   def send_file(file_path):    '''    发送文件    :param file_name:文件名    :return:    '''    size=os.stat(file_path).st_size    file_name=os.path.basename(file_path)    obj.sendall(bytes(str(size)+','+file_name,encoding='utf-8')) #发送文件大小和文件名    ret=obj.recv(1024)   #接收已经传了多少    r=str(ret,encoding='utf-8')    if r=='s': #文件不存在,从头开始传        has_send=0    else:   #文件存在        has_send=int(r)     with open(file_path,'rb') as f:        f.seek(has_send) #定位到已经传到的位置        while has_send
:') obj.sendall(bytes(a,encoding='utf-8')) if a=='q': break else: ret=obj.recv(1024) #等待客户端发送 r=str(ret,encoding='utf-8') if len(r)==1:#判断是cd结果还是ls的结果(ls只有一个子目录也可以直接打印) print(r) else: li=r.split(',') for i in li: print(i) #打印每一个子目录 def main(usrname,pwd): ret=obj.recv(1024) #等待客户端发送 r=str(ret,encoding='utf-8') print(r) result=before(usrname,pwd)#登陆或注册 if result: usr_file(usrname) while True: a=input('请选择1.传文件 2.执行命令 q退出:') obj.sendall(bytes(str(a),encoding='utf-8')) ret=obj.recv(1024) #确认是否收到a r=str(ret,encoding='utf-8') print(r) if a=='1': b=input('请输入文件路径(测试版路径为:f.png):') # b='f.png' if os.path.exists(b): send_file(b) obj.sendall(bytes('hhe',encoding='utf-8')) # obj.recv(1024) elif a=='2': b=input('请输入command:') command(b) elif a=='q': break else: print('输入错误') obj.close() if __name__ == '__main__': obj=socket.socket() #创建客户端socket对象 obj.connect(('127.0.0.1',9999)) usrname=input('请输入用户名') pwd=input('请输入密码') main(usrname,pwd)  

 

转载地址:http://cifua.baihongyu.com/

你可能感兴趣的文章
linux运维人员的成功面试总结案例分享
查看>>
Windows DHCP Server基于MAC地址过滤客户端请求实现IP地址的分配
查看>>
命令查询每个文件文件数
查看>>
《跟阿铭学Linux》第8章 文档的压缩与打包:课后习题与答案
查看>>
RAC表决磁盘管理和维护
查看>>
HDU 3622 Bomb Game(二分+2-SAT)
查看>>
Apache通过mod_php5支持PHP
查看>>
发布一个TCP 吞吐性能测试小工具
查看>>
java学习:jdbc连接示例
查看>>
PHP执行批量mysql语句
查看>>
Extjs4.1.x 框架搭建 采用Application动态按需加载MVC各模块
查看>>
Silverlight 如何手动打包xap
查看>>
建筑电气暖通给排水协作流程
查看>>
JavaScript面向对象编程深入分析(2)
查看>>
linux 编码转换
查看>>
POJ-2287 Tian Ji -- The Horse Racing 贪心规则在动态规划中的应用 Or 纯贪心
查看>>
Windows8/Silverlight/WPF/WP7/HTML5周学习导读(1月7日-1月14日)
查看>>
关于C#导出 文本文件
查看>>
使用native 查询时,对特殊字符的处理。
查看>>
maclean liu的oracle学习经历--长篇连载
查看>>