Python实现终端FTP文件传输

实现终端FTP文件传输

代码结构:

.
├── client.py
├── readme.txt
└── server.py

运行截图:

Python实现终端FTP文件传输

readme.txt

tftp文件服务器

项目功能:
* 客户端有简单的页面命令提示
* 功能包含:
1、查看服务器文件库中的文件列表(普通文件) -> os.listdir
2、可以下载其中的某个文件到本地
3、可以上传客户端文件到服务器文件库
* 服务器需求:
1、允许多个客户端同时操作
2、每个客户端可能会连续发送命令 技术分析:
1、TCP套接字更适合文件传输
2、并发方案 -> fork多进程并发
3、对文件的读写操作
4、获取文件列表 -> os.listdir() 或 tree
5、粘包的处理 整体结构设计:
1、服务器功能封装在类中(上传,下载,查看列表)
2、创建套接字,流程函数调用main()
3、客户端负责发起请求,接收回复,展示
4、服务端负责接受请求,逻辑处理 编程实现:
1、搭建整体结构,创建网络连接
2、创建多进程和类的结构
3、每个功能模块的实现 模块方法:
os.listdir(path)
os.path.isfile()
os.path.isdir()

server.py

# server.py

import struct
from socket import *
import os
import signal
import sys
import time # 文件库
FILE_PATH = '/home/noon/Python/Example/' # 实现功能模块
class TftpServer(object):
def __init__(self, sockfd, addr):
super().__init__()
self.sockfd = sockfd
self.addr = addr
self.opt = '' def display(self):
re = ''
for i in os.listdir(FILE_PATH):
re += i + '\n'
self.sockfd.send(re.encode()) def download(self):
'下载模块功能实现'
# 尝试打开文件
filename = FILE_PATH + self.opt.split(' ')[1]
print(filename)
try:
fp = open(filename, 'rb')
except:
self.sockfd.send(b'Failed to open file')
else:
self.sockfd.send(b'Ready to transfer')
# 循环发送数据
while True:
data = fp.read(1024)
if not data:
# 如果传输完毕,data为空,传输0,跳出循环
res = struct.pack('i', 0)
self.sockfd.send(res)
break
res = struct.pack('i', len(data))
self.sockfd.send(res)
self.sockfd.send(data)
print('Done!') def upload(self):
filename = FILE_PATH + self.opt.split(' ')[1]
try:
fp = open(filename, 'wb')
except:
self.sockfd.send('Unable to open file'.encode())
else:
self.sockfd.send(b'Ready to upload')
while True:
res = self.sockfd.recv(4)
length = struct.unpack('i', res)[0]
if length == 0:
break
data = self.sockfd.recv(length)
fp.write(data)
fp.close()
print('Done!') def quit(self):
print(self.addr, '断开连接')
self.sockfd.close()
sys.exit() # 主流程
def main():
HOST = '0.0.0.0'
PORT = 5555
ADDR = (HOST, PORT) sockfd = socket()
sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
sockfd.bind(ADDR)
sockfd.listen(5) # 通知内核对子进程的结束不关心,由内核回收。
signal.signal(signal.SIGCHLD, signal.SIG_IGN) while True:
try:
connfd, addr = sockfd.accept()
except KeyboardInterrupt:
sockfd.close()
sys.exit('服务器退出')
except Exception as e:
print(e)
continue print('连接成功:', addr) # 创建子进程
pid = os.fork() if pid == 0:
sockfd.close()
tftp = TftpServer(connfd, addr)
while True:
tftp.opt = connfd.recv(1024).decode()
if tftp.opt == 'display':
tftp.display()
elif tftp.opt.startswith('download'):
tftp.download()
elif tftp.opt.startswith('upload'):
tftp.upload()
elif tftp.opt == 'quit':
tftp.quit()
else:
connfd.close()
continue if __name__ == '__main__':
main()

client.py

# client.py

from socket import *
import sys
import time
import struct # 实现各种功能请求
class TftpClient(object):
def __init__(self, sockfd):
super().__init__()
self.sockfd = sockfd
self.opt = '' def panel(self):
print('+', '*'*30, '+', sep='')
print('+', 'display'.center(30), '+', sep='')
print('+', 'download'.center(30), '+', sep='')
print('+', 'upload'.center(30), '+', sep='')
print('+', 'quit'.center(30), '+', sep='')
print('+', '*'*30, '+', sep='') def display(self):
self.sockfd.send(b'display')
print(self.sockfd.recv(1024).decode()) def download(self):
'客户端下载请求'
# 先使用display命令向服务器请求文件列表,验证用户想要下载的文件是否存在
filename = input('filename>> ')
if not filename:
return
self.sockfd.send(b'display')
files = self.sockfd.recv(1024).decode().split('\n')
if not filename in files:
print('Cannot locate', filename)
return
# 文件存在,发送下载请求到服务端,并接收返回结果
data = 'download ' + filename
self.sockfd.send(data.encode())
data = self.sockfd.recv(1024).decode()
# 如果服务端无法打开文件
if data == 'Failed to open file':
print('Failed to open file')
# 可以执行下载操作
else:
# 调用写方法
print(data)
self.write(filename)
print('Done!') def write(self, filename):
'从服务器下载文件'
# 考虑到粘包问题,导入struct模块,接收服务端要发送的数据的大小,再按照这个大小接收数据,循环执行
fp = open(filename, 'wb')
while True:
# 接收数据大小,调用struct.unpack方法获得数据大小
res = self.sockfd.recv(4)
length = struct.unpack('i', res)[0]
# 如果数据大小为0,说明传输结束,退出循环
if length == 0:
break
# 按照数据的大小接收数据
data = self.sockfd.recv(length)
fp.write(data)
fp.close() def upload(self):
# 文件路径
filepath = input('filepath>> ')
try:
fp = open(filepath, 'rb')
except:
print('Unable to open', filepath)
return
else:
# 文件上传要保存为什么名字
# 先使用display命令向服务器请求文件列表,验证用户想要上传的文件名是否存在
filename = input('filename>> ')
if not filename:
return
self.sockfd.send(b'display')
files = self.sockfd.recv(1024).decode().split('\n')
if filename in files:
print('File already exists!')
return
# 可以上传
data = 'upload ' + filename
self.sockfd.send(data.encode())
data = self.sockfd.recv(1024).decode()
if data == 'Unable to open file':
print('服务器打开文件出错')
return
else:
self.read(fp) def read(self, fp):
'读取文件上传服务器'
while True:
data = fp.read(1024)
if not data:
res = struct.pack('i', 0)
self.sockfd.send(res)
break
res = struct.pack('i', len(data))
self.sockfd.send(res)
self.sockfd.send(data)
print('Done!') def quit(self):
self.sockfd.send(b'quit')
self.sockfd.close()
sys.exit('客户端关闭') # 创建套接字,建立连接
def main():
argc = len(sys.argv)
if argc != 3:
sys.exit('Usage: python client.py host port')
else:
HOST = sys.argv[1]
PORT = int(sys.argv[2])
ADDR = HOST, PORT sockfd = socket()
try:
sockfd.connect(ADDR)
except ConnectionRefusedError:
sys.exit('无法连接到服务端') tftp = TftpClient(sockfd) tftp.panel()
while True:
try:
tftp.opt = input('>> ').lower()
except KeyboardInterrupt:
tftp.quit()
if tftp.opt == 'display':
tftp.display()
elif tftp.opt == 'download':
tftp.download()
elif tftp.opt == 'upload':
tftp.upload()
elif tftp.opt == 'quit':
tftp.quit()
else:
continue if __name__ == '__main__':
main()
上一篇:(大数据工程师学习路径)第一步 Linux 基础入门----Linux 下软件安装


下一篇:软件测试用例编写建议