一、要求

1、用户md5认证

2、支持多用户同时登陆(并发)

3、进入用户的命令行模式,支持cd切换目录,ls查看目录子文件

4、执行命令(ipconfig)

5、传输文件:

    a、支持断点续传

    b、传输中显示进度条

二、思路

1.客户端用户登录和注册:

a、客户端仅提供用户名和密码,选择登录或注册,
b、服务器端进行注册并将加密后的密码写入文件,最后返回给客户端是否登录或注册成功

2.ls和cd命令

a、客户端输入命令,服务器端处理并返回给客户端

3.执行命令:

a、客户端发送需要执行的命令
b、服务器端执行命令,并返回客户端需要接收该命令的次数s=r[0]+1,其中r=divmod(结果总长度,1024)
c、客户端收到次数,告诉服务端已经收到
d、服务端发送执行结果,客户端进行for循环接收该结果

4.发送文件:

a、客户端输入文件路径(测试版路径为:f.png),发送文件名和文件大小
b、服务器端检测指定目录是否含有该文件,如果没有,返回给客户端字符串s,即从头开始发送start,has_recv=0
如果有,即需要断点续传,返回给客户端已经上传了多少has_recv
c、客户端接收返回值,并seek到has_recv的位置,进行循环收发,打印当前进度,直到传输完毕。

注:本程序可循环接收用户选择传输文件和执行命令

三、代码

配置文件:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os BASE_DIR = os.path.dirname(os.path.dirname(__file__)) #配置文件的上层目录
NEW_FILENAME=os.path.join(BASE_DIR,'view') #新文件目录
NAME_PWD=os.path.join(BASE_DIR,'db','name_pwd') #用户名和密码目录
USER_FILE=os.path.join(BASE_DIR,'db')

服务器端:

#!/usr/bin/env python
# -*- coding: utf-8 -*- import sys,os
import time
import socket
import hashlib
import pickle
import subprocess
import socketserver
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from config import settings new=settings.NEW_FILENAME
class Myserver(socketserver.BaseRequestHandler): def recv_file(self):
'''
文件传输
:return:
'''
conn=self.request
a=str(conn.recv(1024),encoding='utf-8')
file_size,file_name=a.split(',')
new_file_name=os.path.join(new,file_name)
if file_name in new: #检测文件是否已存在,涉及断点续传
has_recv=os.stat(new).st_size #计算临时文件大小
conn.sendall(bytes(has_recv,encoding='utf-8'))
with open(new_file_name,'ab') as f: #追加模式
while has_recv<=int(file_size):
data=conn.recv(1024)
f.write(data)
has_recv+=len(data)
else:
has_recv=0
conn.sendall(bytes('s',encoding='utf-8')) # 客户端收到字符串s,从0开始发送
with open(new_file_name,'wb') as f:
while has_recv<=int(file_size):
data=conn.recv(1024)
f.write(data)
has_recv+=len(data) def command(self):
'''
执行命令
:return:
'''
conn=self.request
a=conn.recv(1024)
ret=str(a,encoding='utf-8')
ret2 = subprocess.check_output(ret, shell=True)
r=divmod(len(ret2),1024)
s=r[0]+1 #客户端需要接收的次数
conn.sendall(bytes(str(s),encoding='utf-8'))
conn.recv(1024) #确认客户端收到需要接收的次数 conn.sendall(ret2) def md5(self,pwd):
'''
对密码进行加密
:param pwd: 密码
:return:
'''
hash=hashlib.md5(bytes('xx7',encoding='utf-8'))
hash.update(bytes(pwd,encoding='utf-8'))
return hash.hexdigest() def login(self,usrname,pwd):
'''
登陆
:param usrname: 用户名
:param pwd: 密码
:return:是否登陆成功
'''
conn=self.request
s=pickle.load(open(settings.NAME_PWD,'rb'))
if usrname in s:
if s[usrname]==self.md5(pwd): #和加密后的密码进行比较
return True
else:
return False
else:
return False def regist(self,usrname,pwd):
'''
注册
:param usrname: 用户名
:param pwd: 密码
:return:是否注册成功
''' conn=self.request
s=pickle.load(open(settings.NAME_PWD,'rb'))
if usrname in s:
return False
else:
s[usrname]=self.md5(pwd)
mulu=os.path.join(settings.USER_FILE,usrname)
os.makedirs(mulu,'a')
pickle.dump(s,open(settings.NAME_PWD,'wb'))
return True def before(self,usrname,pwd,ret):
'''
判断注册和登陆,并展示用户的详细目录信息,支持cd和ls命令
:return:
'''
conn=self.request
if ret=='1':
r=self.login(usrname,pwd)
if r:
conn.sendall(bytes('y',encoding='utf-8'))
else:
conn.sendall(bytes('n',encoding='utf-8'))
elif ret=='2':
# print(usrname,pwd)
r=self.regist(usrname,pwd)
if r:
conn.sendall(bytes('y',encoding='utf-8'))
else:
conn.sendall(bytes('n',encoding='utf-8'))
def usr_file(self,usrname):
'''
展示用户的详细目录信息,支持cd和ls命令
:param usrname: 用户名
:return:
'''
conn=self.request
conn.recv(1024)
mulu=os.path.join(settings.USER_FILE,usrname)
conn.sendall(bytes(mulu,encoding='utf-8'))
while True:
b=conn.recv(1024)
ret=str(b,encoding='utf-8')
try:
a,b=ret.split(' ',1)
except Exception as e:
a=ret
if a=='cd':
if b=='..':
mulu=os.path.dirname(mulu)
else:
mulu=os.path.join(mulu,b)
conn.sendall(bytes(mulu,encoding='utf-8'))
elif a=='ls':
ls=os.listdir(mulu)
print(ls)
a=','.join(ls)
conn.sendall(bytes(a,encoding='utf-8'))
elif a=='q':
break def handle(self):
conn=self.request
conn.sendall(bytes('welcome',encoding='utf-8'))
b=conn.recv(1024)
ret=str(b,encoding='utf-8')
print(ret)
conn.sendall(bytes('b ok',encoding='utf-8'))
c=conn.recv(1024)
r=str(c,encoding='utf-8')
usrname,pwd=r.split(',')
self.before(usrname,pwd,ret) #登陆或注册验证
self.usr_file(usrname) #展示用户的详细目录信息,支持cd和ls命令
while True:
a=conn.recv(1024)
conn.sendall(bytes('收到a',encoding='utf-8'))
ret=str(a,encoding='utf-8')
if ret=='1':
self.recv_file()
# conn.sendall(bytes('file ok',encoding='utf-8'))
elif ret=='2':
self.command()
elif ret=='q':
break
else:
pass if __name__=='__main__':
sever=socketserver.ThreadingTCPServer(('127.0.0.1',9999),Myserver)
sever.serve_forever()

客户端:

#!/usr/bin/env python
# -*- coding: utf-8 -*- import sys
import time
import os
import socket
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
from config import settings def send_file(file_path):
'''
发送文件
:param file_name:文件名
:return:
'''
size=os.stat(file_path).st_size
file_name=os.path.basename(file_path)
obj.sendall(bytes(str(size)+','+file_name,encoding='utf-8')) #发送文件大小和文件名
ret=obj.recv(1024) #接收已经传了多少
r=str(ret,encoding='utf-8')
if r=='s': #文件不存在,从头开始传
has_send=0
else: #文件存在
has_send=int(r) with open(file_path,'rb') as f:
f.seek(has_send) #定位到已经传到的位置
while has_send<size:
data=f.read(1024)
obj.sendall(data)
has_send+=len(data)
sys.stdout.write('\r') #清空文件内容
time.sleep(0.2)
sys.stdout.write('已发送%s%%|%s' %(int(has_send/size*100),(round(has_send/size*40)*'★')))
sys.stdout.flush() #强制刷出内存
print("上传成功\n") def command(command_name):
'''
执行命令
:param command_name:
:return:
'''
obj.sendall(bytes(command_name,encoding='utf-8'))
ret=obj.recv(1024) #接收命令需要接收的次数
obj.sendall(bytes('收到次数',encoding='utf-8'))
r=str(ret,encoding='utf-8')
for i in range(int(r)): #共需要接收int(r)次
ret=obj.recv(1024) #等待客户端发送
r=str(ret,encoding='GBK')
print(r) def login(usrname,pwd):
'''
登陆
:param usrname:用户名
:param pwd:密码
:return:是否登陆成功
'''
obj.sendall(bytes(usrname+','+pwd,encoding='utf-8'))
ret=obj.recv(1024)
r=str(ret,encoding='utf-8')
if r=='y':
return 1
else:
return 0 def regist(usrname,pwd):
'''
注册
:param usrname:用户名
:param pwd:密码
:return:是否注册成功
'''
obj.sendall(bytes(usrname+','+pwd,encoding='utf-8'))
ret=obj.recv(1024)
r=str(ret,encoding='utf-8')
if r=='y':
return 1
else:
return 0
def before(usrname,pwd):
'''
选择登陆或注册,展示用户的详细目录信息,支持cd和ls命令
:return:
'''
a=input('请选择1.登陆 2.注册')
obj.sendall(bytes(a,encoding='utf-8'))
obj.recv(1024)
if a=='1':
ret=login(usrname,pwd)
if ret:
print('登陆成功')
return 1
else:
print('用户名或密码错误')
return 0
elif a=='2':
ret=regist(usrname,pwd)
if ret:
print('注册成功')
return 1
else:
print('用户名已存在')
return 0
def usr_file(usrname):
obj.sendall(bytes('打印用户文件路径',encoding='utf-8'))
ret=obj.recv(1024) #等待客户端发送
r=str(ret,encoding='utf-8')
print(r)
while True:
a=input('输入cd切换目录,ls查看目录详细信息,q退出>:') obj.sendall(bytes(a,encoding='utf-8'))
if a=='q':
break
else:
ret=obj.recv(1024) #等待客户端发送
r=str(ret,encoding='utf-8')
if len(r)==1:#判断是cd结果还是ls的结果(ls只有一个子目录也可以直接打印)
print(r)
else:
li=r.split(',')
for i in li:
print(i) #打印每一个子目录 def main(usrname,pwd):
ret=obj.recv(1024) #等待客户端发送
r=str(ret,encoding='utf-8')
print(r)
result=before(usrname,pwd)#登陆或注册
if result:
usr_file(usrname)
while True:
a=input('请选择1.传文件 2.执行命令 q退出:')
obj.sendall(bytes(str(a),encoding='utf-8'))
ret=obj.recv(1024) #确认是否收到a
r=str(ret,encoding='utf-8')
print(r)
if a=='1':
b=input('请输入文件路径(测试版路径为:f.png):')
# b='f.png'
if os.path.exists(b):
send_file(b)
obj.sendall(bytes('hhe',encoding='utf-8'))
# obj.recv(1024)
elif a=='2':
b=input('请输入command:')
command(b)
elif a=='q':
break
else:
print('输入错误') obj.close() if __name__ == '__main__':
obj=socket.socket() #创建客户端socket对象
obj.connect(('127.0.0.1',9999))
usrname=input('请输入用户名')
pwd=input('请输入密码')
main(usrname,pwd)

  

  

  

  

python实现支持并发、断点续传的Ftp程序的更多相关文章

  1. (转)Python开发程序:支持多用户在线的FTP程序

    原文链接:http://www.itnose.net/detail/6642756.html 作业:开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ...

  2. 老男孩python作业7-开发一个支持多用户在线的FTP程序

    作业6:开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ,且只能访问自己的家目录 对用户进行磁盘配额,每个用户的可用空间不同 允许用户在ftp s ...

  3. Python3学习之路~8.6 开发一个支持多用户在线的FTP程序-代码实现

    作业: 开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ,且只能访问自己的家目录 对用户进行磁盘配额,每个用户的可用空间不同 允许用户在ftp s ...

  4. Python开发【项目】:FTP程序

    作业:开发一个支持多用户在线的FTP程序 要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ,且只能访问自己的家目录 对用户进行磁盘配额,每个用户的可用空间不同 允许用户在ftp se ...

  5. Python实现支持并发、断点续传的FTP

    参考网上一个FTP程序,重写了一遍,并稍加扩展 一.要求 1. 支持多用户同时登录 2. 可以注册用户,密码使用md5加密 3. 可以登录已注册用户 4.  支持cd切换目录,ls查看目录子文件 5. ...

  6. 实现支持多用户在线的FTP程序(C/S)

    1. 需求 1. 用户加密认证 2. 允许多用户登录 3. 每个用户都有自己的家目录,且只能访问自己的家目录 4. 对用户进行磁盘分配,每一个用户的可用空间可以自己设置 5. 允许用户在ftp ser ...

  7. python如何支持并发?

    由于GIL(Global Interpreter Lock)的存在使得在同一时刻Python进程只能使用CPU的一个核心,也就是对应操作系统的一个 内核线程,对于一个Python web程序,如果有个 ...

  8. 第五十六节,python实现支持并发、断点续传的Ftp程序

    一.要求 1.用户md5认证 2.支持多用户同时登陆(并发) 3.进入用户的命令行模式,支持cd切换目录,ls查看目录子文件 4.执行命令(ipconfig) 5.传输文件: a.支持断点续传 b.传 ...

  9. 开发一个支持多用户在线的FTP程序

    要求: 用户加密认证 允许同时多用户登录 每个用户有自己的家目录 ,且只能访问自己的家目录 对用户进行磁盘配额,每个用户的可用空间不同 允许用户在ftp server上随意切换目录 允许用户查看当前目 ...

随机推荐

  1. 数据流图DFD画法

    数据流图(DFD- Data Flow Diagram)让系统分析者弄清楚"做什么"的问题,其重要性就不言而喻了.那么我们怎么画数据流图呢?数据流图与系统流程图又有什么区别呢? 步 ...

  2. Raspberry Pi 配置笔记二

    配置源 http://blog.chinaunix.net/uid-21658993-id-4702322.html deb http://ipv4.mirrors.ustc.edu.cn/raspb ...

  3. BZOJ 4596: [Shoi2016]黑暗前的幻想乡

    Sol 容斥原理+Matrix-Tree定理.容斥跟小星星那道题是一样的,然后...直接Matrix-Tree定理就可以了... 复杂度\(O(2^{n-1}n^3)\) PS:调了好久啊QAQ 明明 ...

  4. linux下的/dev/shm目录

    linux下的/dev/shm目录 linux中/dev目录下一般都是一些设备文件,例如磁盘.内存.摄像头等. /dev/shm这个目录是linux下一个利用内存虚拟出来的一个目录,这个目录中的文件都 ...

  5. peewee 字段属性help_text的支持问题

    至少在__version__ = '2.6.0'的时候,给字段添加help_text的时候,在数据库的ddl语句里面是没有comment的. 看了下源码,顺藤摸瓜,最终定位到了字段(Field类)的_ ...

  6. redis3.0.0 集群安装详细步骤

    Redis集群部署文档(centos6系统) Redis集群部署文档(centos6系统) (要让集群正常工作至少需要3个主节点,在这里我们要创建6个redis节点,其中三个为主节点,三个为从节点,对 ...

  7. C# 复制(深拷贝、浅拷贝)

    Object.MemberwiseClone 方法 创建当前 Object 的浅表副本. protected Object MemberwiseClone() MemberwiseClone 方法创建 ...

  8. SSRS报表参数设置

    一.日期时间类型的参数注意事项: 关于数据类型的选择:(只有数据类型设置为日期/时间格式,在查询的时候才会显示日期控件,提示信息一般改成汉字) 指定默认值:指定开始日期为前10天,

  9. js隐藏div和class

    <style type="text/css"> //div用点//class# .footer {  display:none;  } #footer {  displ ...

  10. Qt Connect 信号 槽

    信号和槽机制是 QT 的核心机制 .信号和槽是一种高级接口,应用于对象之间的通信,它是 QT 的核心特性,也是 QT 区别于其它工具包的重要地方.信号和槽是 QT 自行定义的一种通信机制,它独立于标准 ...