程序要求:

1.用户加密认证 (对用户名密码进行MD5验证)
2.允许同时多用户登陆 (使用socket server方法,为每个用户都创建一个信息文件)
3.每个用户有自己的家目录,且只能访问自己的家目录(每个用户都建立一个单独的目录)
4.对用户进行磁盘配额,每个用户的可用空间不同(用户信息字典中添加磁盘配额这一参数)
5.允许用户在ftp server 上随意切换目录
6.允许用户查看当前目录下的文件
7.允许上传和下载文件,保证文件的一致性(MD5验证)
8.文件传输过程中显示进度条
9.附加功能:支持文件的断点续传 分析:
数据存储:为每个用户都建立一个文件存储用户名、密码(MD5)、磁盘配额大小,为每一个用户都建立一个家目录
业务逻辑功能:可切换目录、创建目录、可查看目录下的文件、可上传、下载文件(支持断点续传、显示进度条) 开发过程中遇到的问题:
客户端和服务端需要互相传递消息,这样编写一端的程序的时候需要知道另一端程序的结构,
于是要先建立好两端之间大致传递的消息,编写好一端后,再编写另一端 程序目录框架:

程序代码:
README:
程序框架:
|--ftp
|--ftpclinet
|--bin
|--start #程序入口
|--core
|--ftpclient #程序主要逻辑
|--ftpserver
|--bin
|--start #程序入口
|--core
|--main #程序主要逻辑
|--conf
|--settings #定义database、homes的路径
|--homes
|--database
|--modules
|--authendencate #登陆模块
|--socket_server #socket通信模块
|--README ## 状态码
400 用户认证失败
401 命令不正确
402 文件不存在
403 创建文件已经存在
404 磁盘空间不够
405 不续传 200 用户认证成功
201 命令可以执行
202 磁盘空间够用
203 文件具有一致性
205 续传 000 系统交互码
切换目录:cd .. 返回上一级目录 cd dirname 进入dirname
注:用户登录后默认进入家目录,只可在家目录下随意切换 创建目录:mkdir dirname 查看当前路径: pwd 查看当前路径下的文件名和目录名: dir 初始化程序是自动创建用户: {"东云博士":"","马孔多":"","江南":"","白早":""}
ftpclient:
bin目录下的start:
# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu import os,sys
BASE_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_dir)
from core import ftpclient if __name__ == '__main__':
obj = ftpclient.ftp_client(("127.0.0.1",9999))
obj.start()

  core目录下的ftpclient:

# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu
import os,hashlib,socket,json,sys,time class ftp_client(object):
'''ftp client客户端'''
def __init__(self,ip_port):
self.ip_port = ip_port def connect(self):
'''连接套接字地址'''
self.client = socket.socket()
self.client.connect(self.ip_port) def start(self):
'''开始运行'''
self.connect()
while True:
user_name = input("input your name>>>:")
user_password = input("input your password>>>:")
user_msg = "%s:%s"%(user_name,user_password)
self.client.send(user_msg.encode("utf-8"))
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[400]认证失败!")
continue
elif status_code == "":
print("[200]认证成功!")
#self.user_name = user_name
self.interaction() def interaction(self):
'''开始交互'''
while True:
command = input("input command>>>:")
if not command: continue
get_command = command.split()[0]
if hasattr(self,get_command):
func = getattr(self,get_command)
func(command)
else:
print("[401]命令错误!") def get(self,command):
'''下载文件'''
self.client.send(command.encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[401]命令错误")
elif status_code == "":
filename = command.split()[1]
if os.path.isfile(filename):
self.client.send("".encode())
self.client.recv(1024)
received_size = os.stat(filename).st_size
self.client.send(str(received_size).encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[205]可续传!")
self.client.send("".encode())
elif status_code == "":
print("[405]文件完整,不可续传!")
return
else:
self.client.send("".encode())
received_size = 0
print("[205]可下载!")
m = hashlib.md5()
f = open(filename,"wb")
file_total_size = int(self.client.recv(1024).decode())
self.client.send("".encode())
f.seek(received_size)
while received_size < file_total_size:
last_size = file_total_size - received_size
if last_size < 1024:
size = last_size
else:
size = 1024
data = self.client.recv(size)
m.update(data)
f.write(data)
self.__progress_bar(received_size,file_total_size,"下载中")
received_size += len(data)
f.close()
received_md5 = self.client.recv(1024).decode()
md5 = m.hexdigest()
if received_md5 == md5:
print("[203]文件具有一致性!")
else:
print("[403]文件不一致!")
def put(self,command):
'''上传文件'''
self.client.send(command.encode("utf-8"))
status_code = self.client.recv(1024).decode()
if status_code == "":
filename = command.split()[1]
if os.path.isfile(filename):
file_total_size = os.stat(filename).st_size
self.client.send(str(file_total_size).encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
self.client.send("".encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[205]可上传")
elif status_code == "":
print("[404]目录空间不足~")
return
elif status_code == "":
print("[405]文件完整,不续传~")
return
else:
print("[402]文件不存在!")
return self.client.send("".encode())
received_size = int(self.client.recv(1024).decode())
m = hashlib.md5()
f = open(filename,"rb")
f.seek(received_size)
for line in f:
self.client.send(line)
m.update(line)
received_size += len(line)
self.__progress_bar(received_size,file_total_size,"上传中")
f.close()
self.client.send(m.hexdigest().encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[203]文件具有一致性")
else:
print("[403]传输文件不一致!")
elif status_code == "":
print("[401]命令错误!")
def cd(self,command):
'''切换目录'''
self.client.send(command.encode("utf-8"))
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[201]命令执行成功")
elif status_code == "":
print("[401]命令错误")
def dir(self,command):
'''查看当前目录下的文件'''
self.client.send(command.encode("utf-8"))
status_code = self.client.recv(1024).decode()
if status_code == "":
self.client.send("".encode())
data = self.client.recv(1024).decode()
print(data)
elif status_code == "":
print("[401]命令错误!")
def mkdir(self,command):
'''创建目录'''
self.client.send(command.encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
print("[401]命令错误")
elif status_code == "":
print("[403]创建目录已存在!")
elif status_code == "":
print("[201]创建目录成功!")
def pwd(self,command):
'''查看当前用户路径'''
self.client.send(command.encode())
status_code = self.client.recv(1024).decode()
if status_code == "":
self.client.send("".encode())
data = self.client.recv(1024).decode()
print(data)
elif status_code == "":
print("[401]命令错误") def __progress_bar(self,received_size,file_total_size,mode):
width = 50 #进度条长度
percent = received_size/file_total_size #进度条百分比
use_num = int(percent*width) #已使用的进度条长度
space_num = int(width - use_num) #未使用的进度条长度
percent = percent*100
sys.stdout.write("%s[%s%s]%d%%\r"%(mode,use_num*"#",space_num*" ",percent))
sys.stdout.flush()
return

  ftpserver:

       bin目录下的start:

dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASE_dir)
from core import ftpserver if __name__ == '__main__':
ftpserver.My_ftp_server()

       core目录下的ftpserver:

# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu
import sys,os,json,hashlib,socketserver from conf import settings
from modules import socket_server class My_ftp_server(object):
'''ftp初始化'''
def __init__(self):
self.interaction()
def interaction(self):
self.create_user()
self.create_dir()
server = socketserver.ThreadingTCPServer(settings.Ip_port,socket_server.ftp_server)
server.serve_forever()
def create_user(self):
'''创建用户信息文件'''
for key in settings.users_dict:
user_database = {}
name = key
password = settings.users_dict[name]
password = self.hash(password)
user_database["name"] = name
user_database["password"] = password
user_database["limit_size"] = settings.Disk_quota #默认磁盘配额
user_database["home_dir"] = settings.homes_dir + r"\%s"%name #用户家目录
user_db = settings.database_dir + r"\%s.db"%name
if not os.path.isfile(user_db):
f = open(user_db,"w")
json.dump(user_database,f)
f.close() def create_dir(self):
'''创建用户家目录'''
for key in settings.users_dict:
name = key
user_dir = settings.homes_dir + r"\%s"%name
if not os.path.isdir(user_dir):
os.popen("mkdir %s"%user_dir) def hash(self,password):
'''MD5加密'''
m = hashlib.md5()
m.update(password.encode())
return m.hexdigest()

       modules目录下的authendencate:

# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu
import sys,os,json,hashlib from conf import settings class authendencate(object):
'''用户登录模块'''
def __init__(self,user_msg):
self.user_msg = user_msg def auth_user(self):
'''验证用户登陆'''
msg_list = self.user_msg.split(":")
user_name = msg_list[0]
user_password = self.hash(msg_list[1])
user_database = self.get_user_msg(user_name)
if user_database:
if user_name == user_database["name"] and user_password == user_database["password"]:
return user_database def get_user_msg(self,user_name):
'''获取用户信息'''
user_db = settings.database_dir + r"\%s.db"%user_name
if os.path.isfile(user_db):
f = open(user_db,"r")
user_database = json.load(f)
f.close()
return user_database
def hash(self,passwd):
'''md5加密'''
m = hashlib.md5()
m.update(passwd.encode())
return m.hexdigest()

       modules目录下的socket_server:

# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu
import sys,os,socketserver,hashlib
from conf import settings
from modules import authendencate class ftp_server(socketserver.BaseRequestHandler):
'''ftp server端'''
def handle(self):
try:
self.conn = self.request
while True:
user_msg = self.conn.recv(1024).decode()
auth_result = self.auth(user_msg)
status_code = auth_result[0]
self.conn.send(status_code.encode("utf-8"))
if status_code == "": continue
elif status_code == "":
user_database = auth_result[1]
self.user_dir = user_database["home_dir"] #用户默认目录
self.home_dir = user_database["home_dir"] #用户家目录
self.limit_size = user_database["limit_size"] #用户磁盘配额
while True:
command = self.conn.recv(1024).decode()
get_command = command.split()[0]
if hasattr(self,get_command):
func = getattr(self,get_command)
func(command)
else:
self.conn.send("".encode("utf-8"))
except ConnectionResetError as e:
print(e) def get(self,command):
'''下载文件'''
if len(command.split()) > 1:
filename = command.split()[1]
file_db = self.user_dir + r"\%s"%filename
if os.path.isfile(file_db):
file_total_size = os.stat(file_db).st_size
self.conn.send("".encode("utf-8"))
status_code = self.conn.recv(1024).decode()
if status_code == "":
received_size = 0
elif status_code == "":
self.conn.send("".encode())
received_size = int(self.conn.recv(1024).decode())
if received_size < file_total_size:
self.conn.send("".encode())
self.conn.recv(1024)
else:
self.conn.send("".encode())
return
f = open(file_db,"rb")
m = hashlib.md5()
self.conn.send(str(file_total_size).encode())
self.conn.recv(1024)
f.seek(received_size)
for line in f:
self.conn.send(line)
m.update(line)
f.close()
self.conn.send(m.hexdigest().encode())
else:
self.conn.send("".encode("utf-8"))
else:
self.conn.send("".encode("utf-8")) def put(self,command):
'''上传文件'''
if len(command) > 1:
self.conn.send("".encode())
file_name = command.split()[1]
file_db = self.user_dir + r"\%s"%file_name
file_total_size = int(self.conn.recv(1024).decode())
if os.path.isfile(file_db):
received_size = os.stat(file_db).st_size
if received_size < file_total_size:
self.conn.send("".encode())
else:
self.conn.send("".encode())
return
else:
received_size = 0
self.conn.send("".encode())
put_size = file_total_size - received_size
status_code = self.__get_size(put_size)
self.conn.recv(1024)
if status_code == "":
self.conn.send("".encode())
else:
self.conn.send("".encode())
return
self.conn.recv(1024)
self.conn.send(str(received_size).encode())
m = hashlib.md5()
f = open(file_db,"wb")
f.seek(received_size)
while received_size < file_total_size:
last_size = file_total_size - received_size
if last_size < 1024:
size = last_size
else:
size = 1024
data = self.conn.recv(1024)
received_size += len(data)
f.write(data)
m.update(data)
f.close()
md5 = m.hexdigest()
new_md5 = self.conn.recv(1024).decode()
if md5 == new_md5:
self.conn.send("".encode())
else:
self.conn.send("".encode()) def cd(self,command):
'''切换目录'''
if len(command) > 1:
dir_name = command.split()[1]
dir_path = self.user_dir + r"\%s"%dir_name
if dir_name == ".." :
if len(self.user_dir) > len(self.home_dir):
self.conn.send("".encode())
self.user_dir = os.path.dirname(self.user_dir)
else:
self.conn.send("".encode())
elif os.path.isdir(dir_path):
self.user_dir = dir_path
self.conn.send("".encode()) else:
self.conn.send("".encode())
else:
self.conn.send("".encode())
def dir(self,command):
'''查看当前目录下的文件'''
if command == "dir":
self.conn.send("".encode())
self.conn.recv(1024)
data = os.popen("dir %s"%self.user_dir)
self.conn.send(data.read().encode())
else:
print("".encode())
def mkdir(self,command):
'''创建目录'''
if len(command) > 1:
filename = command.split()[1]
dir_path = self.user_dir + r"\%s"%filename
if not os.path.isdir(dir_path):
self.conn.send("".encode())
os.popen("mkdir %s"%dir_path)
else:
self.conn.send("".encode())
else:
self.conn.send("".encode())
def pwd(self,command):
'''查看当前用户路径'''
if command == "pwd":
self.conn.send("".encode())
data = self.user_dir
self.conn.recv(1024)
self.conn.send(data.encode())
else:
self.conn.send("".encode()) def auth(self,user_msg):
'''用户登陆'''
obj = authendencate.authendencate(user_msg)
auth_result = obj.auth_user()
if not auth_result:
return ""
else:
return "",auth_result def __get_size(self,file_size):
'''计算目录空间大小'''
dir_size = os.stat(self.home_dir).st_size + file_size
if dir_size < self.limit_size:
return ""
else:
return ""

       conf目录下的settings:

# -*- coding:utf-8 -*-
#!/user/bin/env.python
#Author:Mr Wu
import os,sys
BASE_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
'''用户信息的属主目录'''
database_dir = os.path.join(BASE_dir,"database")
'''用户目录的属主目录'''
homes_dir = os.path.join(BASE_dir,"homes")
'''用户信息字典'''
users_dict = {"东云博士":"","马孔多":"","江南":"","白早":""}
'''默认磁盘配额'''
Disk_quota = 102400
'''ip地址和端口'''
Ip_port = ("0.0.0.0",9999)

程序运行示例:

断点续传:



python项目开发:ftp server开发的更多相关文章

  1. 【ArcGIS Server 开发系列】Flyingis六大系列讲座精品PDF奉献

    转自:http://www.cnblogs.com/gispeng/archive/2008/07/24/1250116.html [ArcGIS Server 开发系列]Flyingis六大系列讲座 ...

  2. 笔记14:Docker 部署Python项目

    Docker 部署Python项目 导读: 软件开发最大的麻烦事之一就是环境配置,操作系统设置,各种库和组件的安装.只有它们都正确,软件才能运行.如果从一种操作系统里面运行另一种操作系统,通常我们采取 ...

  3. (数据科学学习手札121)Python+Dash快速web应用开发——项目结构篇

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  4. Eclipse开发Python项目

    最近倒腾python自带的开发工具idle,用的很不习惯,还是用Eclipse编写python项目方便(自动补齐,智能报错,调试方便),下面就说说怎么用Eclipse编写python代码吧~ 1.安装 ...

  5. Python+USB+Vnet+FTP传输文件开发记录

    做一个Python+USB+Vnet+FTP传输文件开发记录

  6. python项目开发视频

    精品Python项目开发学习视频 所属网站分类: 资源下载 > python视频教程 作者:乐天派 链接:http://www.pythonheidong.com/blog/article/44 ...

  7. IDEA 学习笔记之 Python项目开发

    Python项目开发: 下载Python: https://www.python.org/downloads/release/python-363/ 安装Python: 配置环境变量(path): C ...

  8. Node.js 从零开发 web server博客项目[express重构博客项目]

    web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...

  9. Node.js 从零开发 web server博客项目[数据存储]

    web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...

随机推荐

  1. Cocos2d-x 开发神器cococreator使用介绍

    Cocos2d-x 开发神器cococreator使用介绍 本篇博客小巫给大家推荐一个开发神器,你还在为搭建Cocos2d-x开发环境而头痛么.还在为平台移植问题而困扰么,我想大家都想更加高速得进行开 ...

  2. HDU5195 线段树+拓扑

    DZY Loves Topological Sorting Problem Description A topological sort or topological ordering of a di ...

  3. c# winform 多条件查找 外加网络人才回答

    浮生 Э 2012-11-22  c# winform 多条件查找  20 我现在有2个textbox  一个是用户名,另一个是电话   现在想对这两个进行条件查找datagridview里的数据 s ...

  4. leetcode数学相关

    目录 166分数到小数 169/229求众数 238除自身以外数组的乘积 69Sqrt(x) 求平方根 231Power of Two 166分数到小数 给定两个整数,分别表示分数的分子 numera ...

  5. vs code golang代码自动补全

    “go.useCodeSnippetsOnFunctionSuggest”: true 文件-->首选项--->设置--->用户设置 添加下行:然后就可以自动补全了,包括() “go ...

  6. js getyear和getfullyear

    getyear()函数如果今年是2015年会得到115,getfullyear()会得到完整的年份.

  7. Gym - 101981K The 2018 ICPC Asia Nanjing Regional Contest K.Kangaroo Puzzle 暴力或随机

    题面 题意:给你1个20*20的格子图,有的是障碍有的是怪,你可以每次指定上下左右的方向,然后所有怪都会向那个方向走, 如果2个怪撞上了,就融合在一起,让你给不超过5w步,让所有怪都融合 题解:我们可 ...

  8. go的基础数据类型

    一.基础数据类型 在go语言中,数据类型用于申明函数和变量 go语言的类型 数据类型 描述 布尔型 布尔型值的只能是true 和 false ,例如 var b bool = true, 布尔型值声明 ...

  9. CodeForces 680A&680B&680C&680D Round#356

    昨天晚上实在是=_=困...(浪了一天)就没有去打Codeforces 中午醒来看看题,还不太难. A题:模拟(水题 3minAC) // by Sirius_Ren #include <cst ...

  10. Could not create the view: An unexpected exception was thrown. Myeclipse空间报错

    我的路径D:\MyEclipse 10\.metadata\.plugins\org.eclipse.core.runtime\.settings 我也遇到过这个问题,就是工作空间的问题好像是删除你工 ...