- 技术点分析
- 并发模型:多线程
- 数据传输:tcp
- 结构设计
- 客户端发送请求,打印请求提示界面
- 文件传输功能封装为类
- 功能分析
- 网络搭建
- 查看文件库信息
- 下载文件
- 上传文件
- 客户端退出
- 协议
- L表示请求文件列表
server
"""
ftp 文件服务器
并发网络功能训练
"""
from socket import *
from threading import Thread
import os
from time import sleep
# 全局变量
HOST = '0.0.0.0'
PORT = 8080
ADDR = (HOST, PORT)
FTP = "/home/tarena/FTP/" # 文件库路径
# 将客户端请求功能封装为类
class FtpServer:
def __init__(self, connfd, FTP_PATH):
self.connfd = connfd
self.path = FTP_PATH
def do_list(self):
# 获取文件列表
files = os.listdir(self.path)
if not files:
self.connfd.send("该文件类别为空".encode())
return
else:
self.connfd.send(b'OK')
sleep(0.1)
fs = ''
for file in files:
if file[0] != '.' and \
os.path.isfile(self.path + file):
fs += file + '\n'
self.connfd.send(fs.encode())
def do_get(self, filename):
try:
fd = open(self.path + filename, 'rb')
except Exception:
self.connfd.send('文件不存在'.encode())
return
else:
self.connfd.send(b'OK')
sleep(0.1)
# 发送文件内容
while True:
data = fd.read(1024)
if not data: # 文件结束
sleep(0.1)
self.connfd.send(b'##')
break
self.connfd.send(data)
def do_put(self, filename):
if os.path.exists(self.path + filename):
self.connfd.send("该文件已存在".encode())
return
self.connfd.send(b'OK')
fd = open(self.path + filename, 'wb')
# 接收文件
while True:
data = self.connfd.recv(1024)
if data == b'##':
break
fd.write(data)
fd.close()
# 客户端请求处理函数
def handle(connfd):
# 选择文件夹
cls = connfd.recv(1024).decode()
FTP_PATH = FTP + cls + '/'
ftp = FtpServer(connfd, FTP_PATH)
while True:
# 接受客户端请求
data = connfd.recv(1024).decode()
# 如果客户端断开返回data为空
if not data or data[0] == 'Q':
return
elif data[0] == 'L':
ftp.do_list()
elif data[0] == 'G':
filename = data.split(' ')[-1]
ftp.do_get(filename)
elif data[0] == 'P':
filename = data.split(' ')[-1]
ftp.do_put(filename)
# 网络搭建
def main():
# 创建套接字
sockfd = socket()
sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sockfd.bind(ADDR)
sockfd.listen(5)
print("Listen the port 8080...")
while True:
try:
connfd, addr = sockfd.accept()
except KeyboardInterrupt:
print("退出服务程序")
return
except Exception as e:
print(e)
continue
print("链接的客户端:", addr)
# 创建线程处理请求
client = Thread(target=handle, args=(connfd,))
client.setDaemon(True)
client.start()
if __name__ == "__main__":
main()
client
from socket import *
import sys
import time
# 具体功能
class FtpClient:
def __init__(self, sockfd):
self.sockfd = sockfd
def do_list(self):
self.sockfd.send(b'L') # 发送请求
# 等待回复
data = self.sockfd.recv(128).decode()
# ok表示请求成功
if data == 'OK':
# 接收文件列表
data = self.sockfd.recv(4096)
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':
fd = open(filename, 'wb')
# 接收内容写入文件
while True:
data = self.sockfd.recv(1024)
if data == b'##':
break
fd.write(data)
fd.close()
else:
print(data)
def do_put(self, filename):
try:
f = open(filename, 'rb')
except Exception:
print("没有该文件")
return
# 发送请求
filename = filename.split('/')[-1]
self.sockfd.send(('P ' + filename).encode())
# 等待回复
data = self.sockfd.recv(128).decode()
if data == 'OK':
while True:
data = f.read(1024)
if not data:
time.sleep(0.1)
self.sockfd.send(b'##')
break
self.sockfd.send(data)
f.close()
else:
print(data)
# 发起请求
def request(sockfd):
ftp = FtpClient(sockfd)
while True:
print("\n==========命令选项===========")
print("*********** list ************")
print("********* get file *********")
print("********* put file *********")
print("*********** quit ************")
print("==============================")
cmd = input("输入命令:")
if cmd.strip() == 'list':
ftp.do_list()
elif cmd.strip() == 'quit':
ftp.do_quit()
elif cmd[:3] == 'get':
filename = cmd.strip().split(' ')[-1]
ftp.do_get(filename)
elif cmd[:3] == 'put':
filename = cmd.strip().split(' ')[-1]
ftp.do_put(filename)
# 网络链接
def main():
# 服务器地址
ADDR = ('127.0.0.1', 8080)
sockfd = socket()
try:
sockfd.connect(ADDR)
except Exception as e:
print("链接服务器失败")
return
else:
print("""
************************
Data File Image
************************
""")
cls = input("请选择文件种类:")
if cls not in ['Data', 'File', 'Image']:
print("Sorry input Error!!")
return
else:
sockfd.send(cls.encode())
request(sockfd) # 发送具体请求
if __name__ == "__main__":
main()