zoukankan      html  css  js  c++  java
  • Python实现支持并发、断点续传的FTP

    参考网上一个FTP程序,重写了一遍,并稍加扩展

    一、要求

    1. 支持多用户同时登录

    2. 可以注册用户,密码使用md5加密

    3. 可以登录已注册用户

    4.  支持cd切换目录,ls查看目录子文件

    5. 支持上传、下载文件

    6. 可以执行命令,如:ipconfig

    二、代码

    配置文件

    1 #!/usr/bin/env python
    2 # -*- coding:utf-8 -*-
    3 
    4 import os
    5 
    6 BASE_DIR = os.path.dirname(os.path.dirname(__file__))
    7 BASE_HOME = os.path.join(BASE_DIR, 'home')
    8 NAME_PWD = os.path.join(BASE_DIR, 'db', 'name_pwd')
    9 USER_FILE = os.path.join(BASE_DIR, 'db')

    服务端:

      1 #!/usr/bin/env python
      2 # -*- coding:utf-8 -*-
      3 
      4 import os
      5 import hashlib
      6 import pickle
      7 import subprocess
      8 import socketserver
      9 from config import settings
     10 
     11 baseHome = settings.BASE_HOME
     12 class MyServer(socketserver.BaseRequestHandler):
     13     def recv_file(self):
     14         '''
     15         文件传输
     16         :return:
     17         '''
     18         conn = self.request
     19         a = str(conn.recv(1024),encoding='utf-8')
     20         file_size, file_name = a.split(',')
     21         new_file_name = os.path.join(baseHome, file_name)
     22         if file_name in baseHome:    #检测文件是否已存在,涉及断点续传
     23             has_recv = os.stat(baseHome).st_size     #计算临时文件大小
     24             conn.sendall(bytes(has_recv, encoding='utf-8'))
     25             with open(new_file_name,'ab') as f:     #追加模式
     26                 data = conn.recv(1024)
     27                 f.write(data)
     28                 has_recv += len(data)
     29         else:
     30             has_recv = 0
     31             conn.sendall(bytes('s',encoding='utf-8'))   #客户端收到字符串s,从0开始发送
     32             with open(new_file_name, 'wb') as f:
     33                 while has_recv<= int(file_size):
     34                     data = conn.recv(1024)
     35                     f.write(data)
     36                     has_recv += len(data)
     37 
     38     def send_file(self, fileName):
     39         '''
     40         向客户端发送文件
     41         :param fileName:
     42         :return:
     43         '''
     44         filePath = os.path.join(self.currDir, fileName)
     45         conn = self.request
     46         if os.path.exists(filePath):
     47             size = os.stat(filePath).st_size
     48             conn.sendall(bytes(str(size)+','+fileName,encoding='utf-8'))
     49             ret = conn.recv(1024)
     50             r = str(ret,encoding='utf-8')
     51             if r=='s':
     52                 has_send = 0
     53             else:
     54                 has_send = int(r)
     55             with open(filePath,'rb') as f:
     56                 f.seek(has_send)
     57                 while has_send<size:
     58                     data = f.read(1024)
     59                     conn.sendall(data)
     60                     has_send+=len(data)
     61             conn.sendall(bytes('0', encoding='utf-8'))
     62         else:
     63             conn.sendall(bytes('0', encoding='utf-8'))
     64 
     65     def createDir(self, currDir, newName):
     66         '''
     67         创建文件夹
     68         :param currDir:当前所在目录
     69         :param newName: 新文件夹名称
     70         :return: 是否创建成功
     71         '''
     72         mulu = os.path.join(baseHome, currDir)
     73         newFilePath = os.path.join(mulu, newName)
     74         if os.path.exists(newFilePath):
     75             return '2'
     76         else:
     77             ret = '0'
     78             try:
     79                 os.makedirs(newFilePath)
     80                 ret = '1'
     81             except OSError as e:
     82                 ret = '0'
     83             return ret
     84 
     85     def command(self):
     86         '''
     87         执行命令
     88         :return:
     89         '''
     90         conn = self.request
     91         a = conn.recv(1024)
     92         ret = str(a, encoding='utf-8')
     93         ret2 = subprocess.check_output(ret, shell=True)
     94         r = divmod(len(ret2), 1024)
     95         s = r[0]+1
     96         conn.sendall(bytes(str(s), encoding='utf-8'))
     97         conn.recv(1024)
     98         conn.sendall(ret2)
     99 
    100     def md5(self, pwd):
    101         '''
    102         判断密码进行加密
    103         :param pwd:
    104         :return:
    105         '''
    106         hash = hashlib.md5(bytes('xx7',encoding='utf-8'))
    107         hash.update(bytes(pwd, encoding='utf-8'))
    108         return hash.hexdigest()
    109 
    110     def login(self, username, pwd):
    111         '''
    112         登录
    113         :param username:用户名
    114         :param pwd: 密码
    115         :return: 是否登录成功
    116         '''
    117         if os.path.exists(settings.NAME_PWD):
    118             s = pickle.load(open(settings.NAME_PWD,'rb'))
    119         if username in s:
    120             if s[username]==self.md5(pwd):
    121                 return True
    122             else:
    123                 return False
    124         else:
    125             return False
    126 
    127     def regist(self, username, pwd):
    128         '''
    129         注册
    130         :param username:用户名
    131         :param pwd: 密码
    132         :return: 是否注册成功
    133         '''
    134         conn = self.request
    135         s = {}
    136         if os.path.exists(settings.NAME_PWD):
    137             s = pickle.load(open(settings.NAME_PWD, 'rb'))
    138         if username in s:
    139             return False
    140         else:
    141             s[username] = self.md5(pwd)
    142             mulu = os.path.join(settings.USER_FILE, username)
    143             os.makedirs(mulu)
    144             pickle.dump(s, open(settings.NAME_PWD, 'wb'))
    145             return True
    146 
    147     def before(self, username, pwd, ret):
    148         '''
    149         判断注册和登录,并展示用户的详细目录信息,支持cd和ls命令
    150         :param username: 用户名
    151         :param pwd: 密码
    152         :param ret:
    153         :return:
    154         '''
    155         conn = self.request
    156         if ret == '1':
    157             r = self.login(username,pwd)
    158             if r:
    159                 conn.sendall(bytes('y',encoding='utf-8'))
    160             else:
    161                 conn.sendall(bytes('n',encoding='utf-8'))
    162         elif ret == '2':
    163             r = self.regist(username, pwd)
    164             if r:
    165                 conn.sendall(bytes('y',encoding='utf-8'))
    166             else:
    167                 conn.sendall(bytes('n',encoding='utf-8'))
    168 
    169     def user_file(self, username):
    170         '''
    171         展示用户的详细目录信息,支持cd和ls命令
    172         :param username: 用户名
    173         :return:
    174         '''
    175         conn = self.request
    176         mulu = baseHome
    177         self.currDir = mulu
    178         conn.sendall(bytes(mulu, encoding='utf-8'))
    179         while True:
    180             if conn:
    181                 b = conn.recv(1024)
    182                 ret = str(b, encoding='utf-8')
    183                 try:
    184                     a, b = ret.split(' ',1)
    185                 except Exception as e:
    186                     a = ret
    187                 if a == 'cd':
    188                     if b=='..':
    189                         mulu = os.path.dirname(mulu)
    190                     else:
    191                         mulu = os.path.join(mulu, b)
    192                     self.currDir = mulu
    193                     conn.sendall(bytes(mulu, encoding='utf-8'))
    194                 elif a=='ls':
    195                     ls = os.listdir(mulu)
    196                     print(ls)
    197                     a = ','.join(ls)
    198                     if a=='':
    199                         a = '.'
    200                     conn.sendall(bytes(a, encoding='utf-8'))
    201                 elif a=='mkdir':
    202                     m = self.createDir(self.currDir,b)
    203                     conn.sendall(bytes(m, encoding='utf-8'))
    204                 elif a=='q':
    205                     break
    206 
    207     def handle(self):
    208         conn = self.request
    209         conn.sendall(bytes('welcome',encoding='utf-8'))
    210         b = conn.recv(1024)
    211         ret = str(b, encoding='utf-8')
    212         c = conn.recv(1024)
    213         r = str(c, encoding='utf-8')
    214         username, pwd = r.split(',')
    215         self.before(username, pwd, ret)
    216         self.user_file(username)
    217         while True:
    218             a=conn.recv(1024)
    219             ret = str(a, encoding='utf-8')
    220             if ret == '1':
    221                 self.recv_file()
    222             elif ret=='2':
    223                 self.command()
    224             elif ret[0:4]=='get:':
    225                 self.send_file(ret[4:])
    226             elif ret=='q':
    227                 break
    228             else:
    229                 pass
    230 
    231 if __name__ == '__main__':
    232     server = socketserver.ThreadingTCPServer(('',9999), MyServer)
    233     server.serve_forever()

    客户端:

      1 #!/usr/bin/env python
      2 # -*-coding:utf-8 -*-
      3 
      4 import os, sys
      5 import socket
      6 
      7 def send_file(file_path):
      8     '''
      9     发送文件
     10     :param file_path:文件名
     11     :return:
     12     '''
     13     size = os.stat(file_path).st_size
     14     file_name = os.path.basename(file_path)
     15     obj.sendall(bytes(str(size)+','+file_name,encoding='utf-8'))
     16     ret = obj.recv(1024)
     17     r = str(ret, encoding='utf-8')
     18     if r=='s':  #文件不存在,从头开始传
     19         has_send = 0
     20     else:   #文件存在
     21         has_send = int(r)
     22     with open(file_path, 'rb') as f:
     23         f.seek(has_send)    #定位到已经传到的位置
     24         while has_send<size:
     25             data = f.read(1024)
     26             obj.sendall(data)
     27             has_send+=len(data)
     28             sys.stdout.write('
    ')  #情况文件内容
     29             sys.stdout.write('已发送%s%%|%s' % (int(has_send/size*100), (round(has_send/size*40)*'|')))
     30             sys.stdout.flush()  #强制刷出内存
     31     print('上传成功!
    ')
     32 
     33 def recv_file(toPath, getFile):
     34     '''
     35     接收要下载的文件
     36     :param toPath: 本地要保存文件的存放路径
     37     :param getFile: 要下载的文件名称
     38     :return:
     39     '''
     40     obj.sendall(bytes('get:'+getFile,encoding='utf-8'))
     41     a = str(obj.recv(1024), encoding='utf-8')
     42     file_size, file_name = a.split(',')
     43     file_size = int(file_size)
     44     if file_size == 0:
     45         print('没有找到此文件')
     46     else:
     47         new_file_name = os.path.join(toPath, file_name)
     48         if file_name in toPath:
     49             has_recv = os.stat(toPath).st_size
     50             obj.sendall(bytes(has_recv, encoding='utf-8'))
     51             with open(new_file_name,'ab') as f:
     52                 while has_recv<=file_size:
     53                     data = obj.recv(1024)
     54                     f.write(data)
     55                     has_recv+=len(data)
     56                     sys.stdout.write('
    ')  # 情况文件内容
     57                     sys.stdout.write('已接收%s%%|%s' % (int(has_recv / file_size * 100), (round(has_recv / file_size * 40) * '|')))
     58                     sys.stdout.flush()  # 强制刷出内存
     59         else:
     60             has_recv = 0
     61             obj.sendall(bytes('s', encoding='utf-8'))
     62             with open(new_file_name, 'wb') as f:
     63                 while has_recv<= file_size:
     64                     data = obj.recv(1024)
     65                     f.write(data)
     66                     has_recv += len(data)
     67                     sys.stdout.write('
    ')  # 情况文件内容
     68                     sys.stdout.write('已接收%s%%|%s' % (int(has_recv / file_size * 100), (round(has_recv / file_size * 40) * '|')))
     69                     sys.stdout.flush()  # 强制刷出内存
     70         print('接收成功!
    ')
     71 
     72 
     73 def command(command_name):
     74     '''
     75     执行命令
     76     :param command_name:
     77     :return:
     78     '''
     79     obj.sendall(bytes(command_name, encoding='utf-8'))
     80     ret = obj.recv(1024)    #接受命令需要接受的次数
     81     obj.sendall(bytes('收到次数',encoding='utf-8'))
     82     r = str(ret, encoding='utf-8')
     83     for i in range(int(r)):     #共需接收int(r)次
     84         ret = obj.recv(1024)    #等待客户端发送
     85         r = str(ret, encoding='GBK')
     86         print(r)
     87 
     88 def login(username, pwd):
     89     '''
     90     登录
     91     :param username: 用户名
     92     :param pwd: 密码
     93     :return: 是否登录成功
     94     '''
     95     obj.sendall(bytes(username+','+pwd, encoding='utf-8'))
     96     ret = obj.recv(1024)
     97     r = str(ret, encoding='utf-8')
     98     if r=='y':
     99         return True
    100     else:
    101         return False
    102 
    103 def regist(username, pwd):
    104     '''
    105     注册
    106     :param username: 用户名
    107     :param pwd: 密码
    108     :return: 是否注册成功
    109     '''
    110     obj.sendall(bytes(username+','+pwd, encoding='utf-8'))
    111     ret = obj.recv(1024)
    112     r = str(ret, encoding='utf-8')
    113     if r=='y':
    114         return True
    115     else:
    116         return False
    117 
    118 def before(username, pwd):
    119     '''
    120     选择注册和登录,并展示用户的详细目录信息,支持cd和ls命令
    121     :param username: 用户名
    122     :param pwd: 密码
    123     :return:
    124     '''
    125     a = input('请选择 1.登录  2.注册:')
    126     obj.sendall(bytes(a, encoding='utf-8'))
    127     # obj.recv()
    128     if a=='1':
    129         ret = login(username, pwd)
    130         if ret:
    131             print('登录成功')
    132             return 1
    133         else:
    134             print('用户名或密码错误')
    135             return 0
    136     elif a=='2':
    137         ret = regist(username, pwd)
    138         if ret:
    139             print('注册成功')
    140             return 1
    141         else:
    142             print('用户名已存在')
    143             return 0
    144 
    145 def user_file(username):
    146     # obj.sendall(bytes('打印用户文件路径', encoding='utf-8'))
    147     ret = obj.recv(1024)
    148     r = str(ret, encoding='utf-8')
    149     print(r)
    150     while True:
    151         a = input('输入 cd切换目录,ls查看目录详细信息,mkdir创建文件夹,q退出:')
    152         a = a.strip()
    153         obj.sendall(bytes(a, encoding='utf-8'))
    154         if a=='q':
    155             break
    156         elif a[0:5]=='mkdir':
    157             ret = obj.recv(1024)
    158             r = str(ret, encoding='utf-8')
    159             if r=='1':
    160                 print('文件夹创建成功')
    161             elif r=='2':
    162                 print('文件夹已存在!')
    163             else:
    164                 print('创建失败!')
    165         else:
    166             ret = obj.recv(1024)
    167             r=str(ret, encoding='utf-8')
    168             if len(r)==1:   #判断是cd结果,还是ls的结果(ls只有一个子目录,直接打印)
    169                 print(r)
    170             else:
    171                 li = r.split(',')
    172                 for i in li:
    173                     print(i)
    174 
    175 def main(username, pwd):
    176     ret = obj.recv(1024)
    177     r = str(ret, encoding='utf-8')
    178     print(r)
    179     result = before(username, pwd)  #判断登录/注册
    180     if result:
    181         user_file(username)
    182         while True:
    183             a = input('请选择 1.传文件 2.执行命令 3.收文件 q 退出:')
    184             obj.sendall(bytes(str(a),encoding='utf-8'))
    185             if a=='1':
    186                 b = input('请输入文件路径:')
    187                 if os.path.exists(b):
    188                     send_file(b)
    189                     obj.sendall(bytes('hhe', encoding='utf-8'))
    190             elif a=='2':
    191                 b = input('请输入command:')
    192                 command(b)
    193             elif a=='3':
    194                 b = input('请输入存放路径:')
    195                 c = input('请输入要获取的文件:')
    196                 recv_file(b, c)
    197             elif a=='q':
    198                 break
    199             else:
    200                 print('输入错误!')
    201 
    202     obj.close()
    203 
    204 if __name__ == '__main__':
    205     obj = socket.socket()
    206     obj.connect(('192.168.1.100',9999))
    207     username = input('请输入用户名:')
    208     pwd = input('请输入密码:')
    209     main(username, pwd)
  • 相关阅读:
    SP503 【PRINT
    UVA10924 【Prime Words】
    UVA902 【Password Search】
    UVA10339 【Watching Watches】
    UVA11057 【Exact Sum】
    DP题单解题报告合集
    P1829 [国家集训队]Crash的数字表格 / JZPTAB
    莫比乌斯反演入门
    莫比乌斯函数
    题解 P3168 [CQOI2015]任务查询系统
  • 原文地址:https://www.cnblogs.com/crucial/p/6106241.html
Copyright © 2011-2022 走看看