python实现简单FTP

  • 需求

1. 用户登陆  ---configparse 存储信息 进行登录验证
2. 上传/下载文件        ---get、put函数
3. 不同用户家目录不同    ----configparse定义家目录
4. 查看当前目录下文件    ----dir命令,如何进行权限判定,只能进入到自己的目录下面?
5. 充分使用面向对象知识    -----就是用类、函数
  • 代码结构

  • 服务端

  1 #!/usr/bin/env python
  2 #-*- coding:utf-8 -*-
  3 # @Time    : 2017/12/14 15:25
  4 # @Author  : lichuan
  5 # @File    : 服务端.py
  6 
  7 
  8 import socketserver
  9 import struct
 10 import json
 11 import subprocess
 12 import os
 13 import configparser
 14 from module import common
 15 import subprocess
 16 
 17 class My_FtpServer(socketserver.BaseRequestHandler):
 18 
 19     def handle(self):
 20         # while True:
 21         try:
 22             name = self.name_check()    #进行用户名验证
 23             self.path = common.get_home_path(name)   #根据用户名获取家目录
 24             if not os.path.exists(self.path):
 25                 os.mkdir(self.path)
 26             # os.chdir(path)
 27             print(os.getcwd())
 28             while True:
 29                 data=self.request.recv(8)   #接收命令长度
 30                 if not data:break   #防止在linux、mac等系统中会有问题
 31                 msg_length=struct.unpack('q',data)[0]  #是一个元组形势的数据,包含命令长度
 32                 cmd = self.request.recv(msg_length).decode('utf-8')
 33                 if cmd.upper() == 'QUIT':  #quit 进行退出
 34                     print('break')
 35                     break
 36                 if hasattr(self,cmd.split()[0]): #收到的消息,进行切分,拿出第一个进行判断
 37                     print(cmd.split()[0])
 38                     func = getattr(self,cmd.split()[0])
 39                     func(cmd)
 40                 else:  #如果没有函数,则进行执行命令
 41                     os.chdir(self.path)
 42                     res=subprocess.Popen(cmd,
 43                                      shell=True,
 44                                      stdout=subprocess.PIPE,
 45                                      stderr=subprocess.PIPE)
 46                     err = res.stderr.read()
 47                     if err:
 48                         back_msg=err
 49                     else:
 50                         back_msg=res.stdout.read()
 51                         if cmd.split()[0] == 'cd':
 52                             new_path = os.getcwd()+os.sep+cmd.split()[1]
 53                             if self.path in new_path:
 54                                 print('self.path',self.path)
 55                                 print('new_path',new_path)
 56                                 self.path = new_path
 57                     print(self.path)
 58                     self.send_msg(back_msg)
 59         except Exception as e:
 60             print("Error:",e)
 61             print('handle exception!')
 62 
 63     def send_header(self,file_info): #发送报头
 64         # file_info = {'cmd': cmd,'status': status}
 65         f_head = json.dumps(file_info)  #json化代码,用于传输
 66         f_head_bytes = bytes(f_head, encoding='utf-8') #变成bytes类型
 67         self.request.send(struct.pack('q', len(f_head_bytes)))  # 传报头长度过去
 68         self.request.sendall(f_head_bytes)  # 传报头过去
 69 
 70     def get(self,cmd):
 71         print('----get function----')
 72         # print(os.getcwd())
 73         os.chdir(self.path)
 74         l = cmd.split()
 75         if len(l) != 2:
 76             print('get command is worng!')
 77             return
 78         # BASE_DIR = os.path.dirname(os.path.abspath(__file__))
 79         f = l[1]
 80         if os.path.exists(f):
 81             file_path = os.path.abspath(f)
 82         else:
 83             print('file not exists!')
 84             return
 85         print('file path:',os.path.abspath(file_path))
 86         if os.path.isfile(file_path) and self.path in file_path:
 87             file_size = os.path.getsize(file_path)
 88             file_md5 = common.GetFileMd5(file_path)  # 获取文件的md5值
 89             status = 'OK'
 90             file_info = {'cmd': cmd, 'filename': os.path.basename(file_path), 'file_size': file_size, 'md5': file_md5,'status':status}
 91             # f_head = json.dumps(file_info)
 92             # print(type(f_head))
 93             # f_head_bytes = bytes(f_head, encoding='utf-8')
 94             # print(f_head_bytes,type(f_head_bytes),type(f_head))
 95             # print('len:',len(f_head))
 96             # self.request.send(struct.pack('q', len(f_head_bytes)))  # 传报头长度过去
 97             # self.request.sendall(f_head_bytes)  # 传报头过去
 98             self.send_header(file_info)
 99             with open(file_path, 'rb') as read_f:
100                 for line in read_f:
101                     self.request.send(line)  # 传文件过去
102         else:
103             print('权限不够或者不是文件')
104             status = 'ERROR'
105             file_info = {'cmd': cmd,'status': status}
106             self.send_header(file_info)
107 
108 
109     def put(self,cmd):
110         print('----put function----')
111         os.chdir(self.path)
112         l = cmd.split()
113         if len(l) != 2:
114             print('put command is worng!')
115             return
116         head_l=self.request.recv(8) #接收报头长度
117         longth = int(struct.unpack('q',head_l)[0]) #接收到的应该是个元组,取第一位,即长度
118         file_json = self.request.recv(longth).decode('utf-8')
119         head_dic = json.loads(file_json)
120         status = head_dic['status']
121         if status == 'OK':
122             file_name=head_dic['filename']
123             file_size=head_dic['file_size']
124             recv_size = 0
125             with open(file_name,'wb') as write_f:
126                 while recv_size < file_size:
127                     res=self.request.recv(1024)
128                     recv_size+=len(res)
129                     write_f.write(res)
130                     print(recv_size,file_size)
131             file_path=os.path.abspath(file_name)
132             # print('file_name:',file_name)
133             print('file_path:',file_path)
134             file_md5=common.GetFileMd5(file_path)
135             # print(file_md5)
136             # print(head_dic['md5'])
137             if file_md5 == head_dic['md5']:
138                 print('文件传输成功')
139             else:
140                 print('文件不一致')
141                 os.remove(file_path)
142 
143     def send_msg(self,msg):#用于发送信息
144         self.head_l = struct.pack('q',len(msg))
145         self.request.send(self.head_l)
146         self.request.sendall(msg)
147 
148     #进行用户名密码验证的函数
149     def name_check(self):
150         while True:
151             name = self.request.recv(1024).decode('utf-8')
152             self.request.send('0'.encode('utf-8'))
153             passwd=self.request.recv(1024).decode('utf-8')
154             if common.login(name,passwd):
155                 self.request.send('0'.encode('utf-8'))
156                 return name
157             else:
158                 self.request.send('1'.encode('utf-8'))
159                 continue
160 
161     #执行dir命令显示目录下的内容
162     def show_path_content(self,path):
163         print('show path'+path)
164         cmd = 'dir ' + path
165         res1 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
166         return res1.stdout.read().decode('gbk')
167 
168 if __name__ == '__main__':
169     fserver = socketserver.ThreadingTCPServer(('127.0.0.1',8001),My_FtpServer)
170     fserver.serve_forever()
171     fserver.server_close()
172     # for l in os.listdir(os.getcwd()):
173     #     print(l)
174     # for i in os.walk(os.path.dirname(os.path.dirname((os.path.abspath(__file__))))):
175     #     print(i)
服务端.py
  • 客户端

  1 #!/usr/bin/env python
  2 #-*- coding:utf-8 -*-
  3 # @Time    : 2017/12/14 15:25
  4 # @Author  : lichuan
  5 # @File    : 客户端.py
  6 
  7 
  8 #_*_coding:utf-8_*_
  9 
 10 import time
 11 import socket
 12 import struct
 13 import json
 14 import os
 15 from module import common
 16 
 17 BUFSIZE=1024
 18 
 19 
 20 class FTP_ClientServer:
 21     def __init__(self,ip_port,logined=False):
 22         self.ip_port=ip_port
 23         self.logined = logined
 24         self.socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 25         try:
 26             self.socket.connect(ip_port)
 27         except:
 28             self.socket.close()
 29             raise
 30 
 31     def run(self):
 32         while True:
 33             if not self.logined:
 34                 self.login()
 35             else:
 36                 msg = input('>>:').strip()
 37                 msg_longth=struct.pack('q',len(msg))
 38                 self.socket.send(msg_longth)
 39                 self.socket.sendall(msg.encode('utf-8'))
 40                 if msg.upper() == 'QUIT':
 41                     break
 42                 if hasattr(self,msg.split()[0]):
 43                     func=getattr(self,msg.split()[0])
 44                     func(msg)
 45                 else:  #传送命令过去的逻辑
 46                     recv = self.socket.recv(8)
 47                     data = struct.unpack('q',recv) #返回的是一个元组,(123,),123是长度
 48                     print(data)
 49                     longth = data[0]
 50                     recv_size = 0
 51                     recv_data = b''
 52                     while recv_size < longth:
 53                         recv_data+=self.socket.recv(1024)
 54                         recv_size+=1024
 55                     print(recv_data.decode('gbk'))
 56 
 57     #程序开始时,进行登录验证的函数
 58     def login(self):
 59         name = input('username:').strip()
 60         passwd = input('password:').strip()
 61         if len(name) == 0: return False
 62         if len(passwd) == 0: return False
 63         self.socket.send(name.encode('utf-8'))  # 发送name过去进行验证
 64         recv = self.socket.recv(1)
 65         if recv.decode('utf-8') != '0':  # name返回结果不为0,说明name不存在
 66             print('name error')
 67             return False
 68         self.socket.send(passwd.encode('utf-8'))
 69         recv = self.socket.recv(1)
 70         if recv.decode('utf-8') != '0':  # passwd返回结果不为0,说明passwd错误
 71             print('passwd error')
 72             return False
 73         print('login successful!')
 74         self.logined = True
 75         return True
 76 
 77     def recv_msg(self):
 78         head_l = self.socket.recv(8)
 79         data_head = struct.unpack('q',head_l)
 80 
 81     def get(self,cmd):
 82         l = cmd.split()
 83         if len(l) != 2:
 84             print('get command is worng!')
 85             return
 86         head_l = self.socket.recv(8)  # 接收报头长度
 87         longth = int(struct.unpack('q', head_l)[0])  # 接收到的应该是个元组,取第一位,即长度
 88         file_json = self.socket.recv(longth).decode('utf-8')
 89         head_dic = json.loads(file_json)
 90         status = head_dic['status']
 91         if status == 'OK':
 92             file_name = head_dic['filename']
 93             file_size = head_dic['file_size']
 94             recv_size = 0
 95             with open(file_name, 'wb') as write_f:
 96                 while recv_size < file_size:
 97                     res = self.socket.recv(1024)
 98                     recv_size += len(res)
 99                     write_f.write(res)
100                     print(recv_size, file_size)
101             file_path = os.path.abspath(file_name)
102             # print('file_name:',file_name)
103             print('file_path:', file_path)
104             file_md5 = common.GetFileMd5(file_path)
105             # print(file_md5)
106             # print(head_dic['md5'])
107             if file_md5 == head_dic['md5']:
108                 print('文件传输成功')
109             else:
110                 print('文件不一致')
111                 os.remove(file_path)
112         else:
113             print('权限不够或者不是文件')
114 
115     def put(self,msg):
116         l=msg.split()
117         if len(l) != 2:
118             print('put command is worng!')
119             return
120         BASE_DIR=os.path.dirname(os.path.abspath(__file__))
121         f=l[1]
122         if os.path.exists(f):
123             file_path = os.path.abspath(f)
124         else:
125             print('file not exists!')
126             return
127         if os.path.isfile(file_path):
128             file_size = os.path.getsize(file_path)
129             file_md5 = common.GetFileMd5(file_path)  #获取文件的md5值
130             status = 'OK'
131             file_info = {'cmd':msg,'filename':os.path.basename(file_path),'file_size':file_size,'md5':file_md5,'status':status}
132             self.send_header(file_info)
133             # f_head=json.dumps(file_info)
134             # print(type(f_head))
135             # f_head_bytes = bytes(f_head,encoding='utf-8')
136             # print(f_head_bytes,type(f_head_bytes),type(f_head))
137             # print('len:',len(f_head))
138             # self.socket.send(struct.pack('q',len(f_head_bytes))) #传报头长度过去
139             # self.socket.sendall(f_head_bytes)     #传报头过去
140             num=0
141             with open(file_path,'rb') as read_f:
142                 for line in read_f:
143                     self.socket.send(line)       #传文件过去
144                     # num+=1
145                     # print(num)
146         else:
147             status = 'ERROR'
148             file_info = {'cmd': msg,'status': status}
149             self.send_header(file_info)
150 
151     def send_header(self,file_info): #发送报头
152         # file_info = {'cmd': cmd,'status': status}
153         f_head = json.dumps(file_info)  #json化代码,用于传输
154         f_head_bytes = bytes(f_head, encoding='utf-8') #变成bytes类型
155         self.socket.send(struct.pack('q', len(f_head_bytes)))  # 传报头长度过去
156         self.socket.sendall(f_head_bytes)  # 传报头过去
157 
158 if __name__ == '__main__':
159     ip_port = ('127.0.0.1', 8001)
160     f=FTP_ClientServer(ip_port)
161     f.run()
客户端.py
  • 用户定义文件

1 [egon]
2 name = egon
3 home_path = egon
4 passwd = bc5b9cb3e4ab483335edab3347f3c102
5 
6 [alex]
7 name = alex
8 home_path = alex
9 passwd = bc5b9cb3e4ab483335edab3347f3c102
users.cfg
  • 公共定义函数文件

 1 #!/usr/bin/env python
 2 #-*- coding:utf-8 -*-
 3 # @Time    : 2017/10/20 15:46
 4 # @Author  : lichuan
 5 # @File    : common.py
 6 
 7 import hashlib
 8 import os
 9 import re
10 from log import my_log_settings
11 import logging
12 import pickle
13 import configparser
14 
15 def login(name,passwd):
16     path = os.path.dirname(os.path.abspath(__file__))+os.sep+'users.cfg'
17     config = configparser.ConfigParser()
18     config.read(path)
19     # print(config.sections())
20     if name not in config.sections():
21         print('name is wrong!')
22         return False
23     encrypt_passwd = encrypt(passwd)
24     # print(encrypt_passwd)
25     p = config.get(name,'passwd')
26     # print(p)
27     if encrypt_passwd == p:
28         print('login successful')
29         return True
30     else:
31         print('password is wrong!')
32         return False
33 
34 #根据name获取家目录路径
35 def get_home_path(name):
36     config = configparser.ConfigParser()
37     path = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'users.cfg'
38     # print(path)
39     config.read(path)
40     # print(config.sections())
41     if name not in config.sections():
42         print('name is not exist')
43         return None
44     else:
45         p = config.get(name,'home_path')
46         new_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + os.sep +'db'+os.sep+p
47         # print(new_path)
48         return new_path
49 
50 def encrypt(str):
51     '''
52     对传入字符串进行加盐加密
53     :param str: 需要进行加密的字符串
54     :return: 返回加密过的字符串
55     '''
56     encrpt=hashlib.md5()
57     encrpt.update(bytes('admin1234nnnnnn',encoding='utf-8'))
58     encrpt.update(bytes(str,encoding='utf-8'))
59     return encrpt.hexdigest()
60 
61 #计算文件的md5值
62 def GetFileMd5(filename):
63      if not os.path.isfile(filename):
64          print('file is not exists')
65          return
66      myhash = hashlib.md5()
67      with open(filename,'rb') as f:
68          while True:
69              b = f.read(8096)
70              if not b :
71                  break
72              myhash.update(b)
73          return myhash.hexdigest()
74 
75 # if __name__ == '__main__':
76 #     a=GetFileMd5('users.cfg')
77 #     print(a)
common.py
  • 执行效果

C:Python35python.exe C:/Users/dell/PycharmProjects/day7_2/bin2/客户端2.py
username:alex
password:123456
login successful!
>>:dir
(458,)
 驱动器 C 中的卷没有标签。
 卷的序列号是 D48A-2DD8

 C:UsersdellPycharmProjectsday7_2dbalex 的目录

2018/01/09  11:02    <DIR>          .
2018/01/09  11:02    <DIR>          ..
2018/01/05  17:18             5,231 aa.txt
2018/01/09  11:02             5,231 bb.txt
2018/01/08  16:46    <DIR>          ddd
2018/01/05  14:43             8,815 test.py
               3 个文件         19,277 字节
               3 个目录 23,366,094,848 可用字节

>>:rm bb.txt
(61,)
'rm' 不是内部或外部命令,也不是可运行的程序
或批处理文件。

>>:del bb.txt
(0,)

>>:dir
(414,)
 驱动器 C 中的卷没有标签。
 卷的序列号是 D48A-2DD8

 C:UsersdellPycharmProjectsday7_2dbalex 的目录

2018/01/09  11:15    <DIR>          .
2018/01/09  11:15    <DIR>          ..
2018/01/05  17:18             5,231 aa.txt
2018/01/08  16:46    <DIR>          ddd
2018/01/05  14:43             8,815 test.py
               2 个文件         14,046 字节
               3 个目录 23,366,037,504 可用字节

>>:put bb.txt
>>:get aa.txt
1024 5231
1546 5231
1605 5231
1646 5231
1698 5231
1700 5231
1752 5231
1774 5231
1817 5231
1862 5231
1903 5231
1946 5231
2026 5231
2062 5231
2152 5231
2185 5231
2211 5231
2261 5231
2297 5231
2388 5231
2423 5231
2449 5231
2485 5231
2514 5231
2535 5231
2537 5231
2562 5231
2600 5231
2647 5231
2649 5231
2673 5231
2698 5231
2723 5231
2767 5231
2787 5231
2847 5231
2961 5231
3023 5231
3065 5231
3107 5231
3150 5231
3173 5231
3221 5231
3263 5231
3309 5231
3348 5231
3384 5231
3429 5231
3477 5231
3518 5231
3558 5231
3607 5231
3634 5231
3668 5231
3709 5231
3750 5231
3765 5231
3803 5231
3837 5231
3839 5231
3863 5231
3886 5231
3911 5231
3955 5231
3975 5231
4036 5231
4083 5231
4127 5231
4142 5231
4181 5231
4201 5231
4240 5231
4292 5231
4369 5231
4482 5231
4524 5231
4559 5231
4618 5231
4685 5231
4726 5231
4815 5231
4883 5231
4902 5231
4952 5231
4989 5231
5056 5231
5084 5231
5116 5231
5118 5231
5146 5231
5181 5231
5214 5231
5227 5231
5229 5231
5231 5231
file_path: C:UsersdellPycharmProjectsday7_2in2aa.txt
文件传输成功
>>:quit
View Code

定义了一个self.path参数,对用户的目录进行限定,以此做权限控制。

原文地址:https://www.cnblogs.com/litzhiai/p/8250528.html