参考网上一个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('\r') #情况文件内容
29 sys.stdout.write('已发送%s%%|%s' % (int(has_send/size*100), (round(has_send/size*40)*'|')))
30 sys.stdout.flush() #强制刷出内存
31 print('上传成功!\n')
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('\r') # 情况文件内容
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('\r') # 情况文件内容
68 sys.stdout.write('已接收%s%%|%s' % (int(has_recv / file_size * 100), (round(has_recv / file_size * 40) * '|')))
69 sys.stdout.flush() # 强制刷出内存
70 print('接收成功!\n')
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)