ftp文件服务器
功能
- 分为服务端和客户端,要求可以有多个客户端同时操作。
- 客户端可以查看服务器文件库中有什么文件。
- 客户端可以从文件库中下载文件到本地。
- 客户端可以上传一个本地文件到文件库。
- 使用print在客户端打印命令输入提示,引导操作
图解
1.技术点确定
- 并发模型: 多线程并发
- 数据传输:tcp传输
2.结构设计
- 将基本文件操作功能封装为类
3.功能模块
- 搭建网络通信模型
- 查看文件列表
- 下载文件
- 上传文件
- 客户端退出
4.协议确定
- L 请求文件列表
- Q 退出
- G 下载文件
- P 上传文件
5.代码演示
服务器端
"""
ftp 文件服务器,服务端
多进程/线程并发, socket
"""
import sys, os, time
from socket import *
from threading import Thread
HOST = '0.0.0.0'
PORT = 26262
ADDR = (HOST, PORT)
FTP = 'FTP/'
class FTPServer(Thread):
"""
查看列表,下载,上传,退出处理
"""
def __init__(self, connfd):
self.connfd = connfd
super().__init__()
def do_list(self):
files = os.listdir(FTP)
if not files:
self.connfd.send('文件库为空'.encode())
else:
self.connfd.send('OK'.encode())
time.sleep(0.5)
filelist = ''
for file in files:
if file[0] != '.' and os.path.isfile(FTP + file):
filelist += file + '\n'
self.connfd.send(filelist.encode())
def do_get(self, filename):
try:
f = open(FTP + filename, 'rb')
except Exception:
self.connfd.send('文件不存在'.encode())
else:
self.connfd.send('OK'.encode())
time.sleep(0.5)
while True:
data = f.read(1024)
if not data:
time.sleep(0.5)
self.connfd.send(b'##')
break
self.connfd.send(data)
f.close()
print('%s传输完毕!'%filename)
def do_put(self, filename):
if os.path.isfile(FTP + os.path.basename(filename)):
self.connfd.send('文件已经存在'.encode())
else:
self.connfd.send('OK'.encode())
time.sleep(0.5)
f = open(FTP + os.path.basename(filename), 'wb')
while True:
data = self.connfd.recv(1024)
if data == '##'.encode():
print('%s接收结束'%os.path.basename(filename))
break
f.write(data)
f.close()
def run(self):
while True:
data = self.connfd.recv(1024).decode()
if not data or data == 'Q':
return
elif data == 'L':
self.do_list()
elif data[0] == 'G':
filename = data.split(' ')[-1]
self.do_get(filename)
elif data[0] == 'P':
filename = data.split(' ')[-1]
self.do_put(filename)
def main():
s = socket()
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(ADDR)
s.listen(5)
while True:
try:
c, addr = s.accept()
print('Connect from', addr)
except KeyboardInterrupt:
sys.exit('退出服务器')
except Exception as e:
print(e)
continue
client = FTPServer(c)
client.setDaemon(True)
client.start()
if __name__ == '__main__':
main()
客户端
"""
ftp 文件服务,客户端
"""
import os
import sys
import time
from socket import *
ADDR = ('119.91.116.248', 26262)
class FTPClient:
"""
客户端处理 查看,上传,下载,退出
"""
def __init__(self, sockfd):
self.sockfd = sockfd
def do_list(self):
self.sockfd.send(b'L')
data = self.sockfd.recv(128).decode()
if data == 'OK':
data = self.sockfd.recv(4096)
print('\n服务器有以下文件可供下载,请输入 get 文件名\n')
print(data.decode())
else:
print(data)
def do_quit(self):
self.sockfd.send(b'Q')
self.sockfd.close()
sys.exit('谢谢使用')
def do_get(self, filename):
self.sockfd.send(('G ' + filename).encode())
data = self.sockfd.recv(128).decode()
if data == 'OK':
f = open(filename, 'wb')
while True:
data = self.sockfd.recv(1024)
if data == b'##':
break
f.write(data)
f.close()
else:
print(data)
def do_put(self, filename):
if os.path.isfile(filename):
self.sockfd.send(('P ' + os.path.basename(filename)).encode())
data = self.sockfd.recv(128).decode()
if data == 'OK':
try:
f = open(filename, 'rb')
except Exception:
print('文件不存在')
else:
for data in f:
self.sockfd.send(data)
time.sleep(0.5)
self.sockfd.send(b'##')
f.close()
print('文件上传成功')
else:
print(data)
else:
print('输入的文件名有误')
def main():
sockfd = socket()
try:
sockfd.connect(ADDR)
except Exception as e:
print(e)
return
ftp = FTPClient(sockfd)
while True:
print('\n=======命令选项========')
print('**********list**********')
print('***** get file *****')
print('***** put file *****')
print('***** quit *****')
print('************************\n')
cmd = input('').strip()
if cmd == 'list':
ftp.do_list()
elif cmd == 'quit':
ftp.do_quit()
break
elif cmd[:3] == 'get':
filename = cmd.split(' ')[-1]
ftp.do_get(filename)
elif cmd[:3] == 'put':
filename = cmd.split(' ')[-1]
ftp.do_put(filename)
else:
print('请输入正确命令')
if __name__ == '__main__':
main()