转载请注明出处http://www.cnblogs.com/Wxtrkbc/p/5590004.html   

  本来最初的想法是实现一个ftp服务器,用来实现用户的登陆注册和文件的断点上传下载等,结果做着做着就连CRT也顺带着跟着完成了,然后就变成了这样一个'不伦不类'的工具。用到的知识有hashlib加密密码传输,面向对象,sockeserver支持多客户访问,os.subprocess来处理系统自带的命令,然后自定义上传下载命令,以及如何实现断点续传,传输过程中的粘包问题,以及如何反射处理用户的输入。这里仅仅是为了娱乐,下面先来看一下效果图

      

        

一、客户端

首先定义以个客户端类,大致在类里面定义了这些方法,以后有需要的话,可以对其进行扩充,下面的类一初始化就会去执行start方法,如果在服务端注册登录成功的话,就会接下来执行internet方法,等待用户的输入,

class Client:
def __init__(self, address):
self.address = Client.get_ip_port(address)
self.help_message = [
'目前自定义的命令仅支持以下操作:\n'
'\tput|filename',
'\tget|filename',
]
self.start()
self.cwd = '' @staticmethod
def get_ip_port(address):
ip, port = address.split(':')
return (ip, int(port)) def register(self):
try_counts = 0
while try_counts < 3:
user = input('请输入用户名:')
if len(user) == 0:
continue
passwd = input('请输入用密码:')
if len(passwd) == 0:
continue
pd = hashlib.sha256()
pd.update(passwd.encode())
self.socket.sendall('register|{}:{}'.format(user, pd.hexdigest()).encode()) # 发送加密后的账户信息
ret = self.socket.recv(1024).decode()
if ret == '202':
print('注册成功请登录')
os.mkdir(os.path.join(settings.USER_HOME_DIR, user)) # 在客户端也创建一个用户家目录
os.mkdir(os.path.join(settings.USER_HOME_DIR, user, 'download_file'))
os.mkdir(os.path.join(settings.USER_HOME_DIR, user, 'upload_file'))
return True
else:
try_counts += 1
sys.exit("Too many attemps") def login(self):
try_counts = 0
while try_counts < 3:
user = input('请输入用户名:')
self.user = user
if len(user) == 0:
continue
passwd = input('请输入用密码:')
if len(passwd) == 0:
continue
pd = hashlib.sha256()
pd.update(passwd.encode())
self.socket.sendall('login|{}:{}'.format(user, pd.hexdigest()).encode()) # 发送加密后的账户信息
ret = self.socket.recv(1024).decode()
if ret == '200':
print('登陆成功!')
self.cwd = self.socket.recv(1024).decode()
return True
else:
print('用户或密码错误,请从新登陆:')
try_counts += 1
sys.exit("Too many attemps") def internet(self):
pass<br>
def process(self, cmd, argv): # 处理自定义的命令
pass<br>
def help(self, argv=None):
pass def put(self, argv=None):
pass def get(self, argv=None):
pass def start(self):
self.socket = socket.socket()
try:
self.socket.connect(self.address)
except Exception as e:
sys.exit("Failed to connect server:%s" % e)
print(self.socket.recv(1024).decode())
inp = input('1、注册,2、登录,3、离开: ')
if inp == '1':
if self.register():
if self.login(): # 登陆成功后进行交互操作
self.internet()
elif inp == '2':
if self.login():
self.internet()
else:
sys.exit() if __name__ == '__main__':
# address = input('请输入FTP服务端地址(ip:port):')
address = '127.0.0.1:9999'
client = Client(address)

二、服务端  

  服务端是用socketserver来写的,以便出来多用户请求,每当用户来来请求的时候,先让其注册或登陆,注册完后,以用户的名字为其创建一个家目录,并将用户名和密码保存起来,将来用户登录的时候从db中取出数据和用户输入的密码进行对比,正确后让其进行下一步操作,用户密码输入三次以后,退出程序。服务端为了响应客户端的每一个操作,定义了一个函数专门接受客户端传来的每一次命令,然后对其分解,反射到具体的服务端具体的函数中去,这里需要先定义客户端传过来的数据的格式是 cmd|args。大家可以看到我上面客户端登陆认证的代码传输的数据格式 ('login|{}:{}'.format(user, pd.hexdigest())) ,这么做的道理就是为了服务端好统一进行处理。下面来一下代码具体怎么做的,

def handle(self):
self.request.sendall('欢迎来到FTP服务器!'.encode())
while True:
data = self.request.recv(1024).decode()
if '|' in data:
cmd, argv = data.split('|')
else:
cmd = data
argv = None
self.process(cmd, argv) # 将接受道德数据出来后在经过 process def process(self, cmd, argv=None): # 使用反射处理客户端传过来的命令(自定义的命令)
if hasattr(self, cmd):
func = getattr(self, cmd)
func(argv)          
else:
pass

这么一写后,就只需在服务端写上相应的函数,比如,注册的话,我就只需写一个函数名为register的函数,专门来处理注册,同理登陆的话,我也只需写一个login函数即可,当初我第一次写的时候,都是用if,else来判断,当时的代码写下来,自己看着都恐怖,写着写着,自己都不知道判断到哪里去了,想想就泪奔。而用反射的话,就不需要这些繁琐的步骤了,而且以后要添加功能的话,只需要写一个简单的函数即可。客户端的反射也这样做的,就不再重复了。

三、断点上传

如果只是文件上传的话,比较好写,但是如果要断点上传的话,就有些麻烦了,这里提供一种思路,那就是服务端纪录以上传文件的大小,下次上传的时候,如果要断点续传的话,先将已上传的文件大小发给客户端,然后客户端从断点的位置在上传。下面来看一下代码的实现,

# 客户端

def put(self, argv=None):
if len(argv) == None:
print("Please add the file path that you want to upload")
return
print('上传之前请确保的文件在用户upload文件夹下')
file_path = os.path.join(settings.USER_HOME_DIR, self.user, 'upload_file', argv)
if os.path.exists(file_path): #判断文件存不存在
file_size = os.stat(file_path).st_size
file_info = {
'file_name': argv,
'file_size': file_size,
}
has_sent = 0 self.socket.sendall(('put|{}'.format(json.dumps(file_info))).encode())  # 将上传的文件信息作为参数发给服务端
ret = self.socket.recv(1024).decode()
if ret == '204':
inp = input("文件存在,是否续传?Y/N:").strip()
if inp.upper() == "Y":
self.socket.sendall('205'.encode())
has_sent = int(self.socket.recv(1024).decode())
else:
self.socket.sendall('207'.encode())
with open(file_path, 'rb') as f:
f.seek(has_sent)          # 如果要续传的话,has_set为已经上传的大小,否则 has_set为0,从头开始上传
for line in f:
self.socket.sendall(line)
has_sent += len(line)
k = int((has_sent / file_size * 100)) # 下面的代码是用来显示进度条
table_space = (100 - k) * ' '
flag = k * '*'
time.sleep(0.05)
sys.stdout.write('\r{} {:.0%}'.format((flag + table_space), (has_sent / file_size)))
print() # 显示换行的作用

下面来看一下服务端的代码,服务端和客户端都是一收一发,注意要保持recv要收到信息,否则会阻塞,此外传送的时候可能会发生粘包,解决的办法是在发送文件文件前,先发送一条标志信息,当服务端收到该标志信息,就可以通知客户端发送文件了。

# 服务端

def put(self, argv=None):
file_info = json.loads(argv)          # 获取客户端传来的消息
file_name = file_info['file_name']
file_size = int(file_info['file_size'])
file_path = os.path.join(settings.USER_HOME_DIR, 'kobe', 'upload_file', file_name)
have_send = 0 # 已经上传的位置 if os.path.exists(file_path):
self.request.sendall('204'.encode())
ret = self.request.recv(1024).decode()
if ret == '205': # 续传
have_send = os.stat(file_path).st_size     # 获取已经上传文件的大小
self.request.sendall(bytes(str(have_send), encoding='utf-8'))
f = open(file_path, 'ab')             # 续传的话,以a模式打开文件,
else:  
f = open(file_path, 'wb')             # 不续传的话,以w模式打开,
else:
self.request.sendall('206'.encode())      # 直接上传
f = open(file_path, 'wb') while True:
if have_send == file_size:              # 一旦接受到的内容等于文件大小,直接退出循环  
break
try:
ret = self.request.recv(1024)
except Exception as e:
break
f.write(ret)
have_send += len(ret)

解决了断点续传,那么断点下载也是同样的道理,就不再重复讲了。

4、CRT 

  最后来讲一下怎么实现远程操作服务端主机,这里存粹是为了娱乐。其实实现起来也比较简单,主要使用了subprocess.getoutput获取来处理客户端输入的命令,然后将结果返回给客户端就可以,但是有一点致命的缺陷,那就是不支持cd命令,如果不支持cd命令的话,那还谈什么远程操作了,所以这里对于cd命令就需要特殊对待了,解决的办法,当然是找一个支持cd的命令,下面来看下服务端大代码

def process(self, cmd, argv=None):  # 使用反射处理客户端传过来的命令(自定义的命令)
if hasattr(self, cmd):
func = getattr(self, cmd)
func(argv)
else:
if cmd.startswith('cd'): #(处理cd命令,subprocess不支持cd命令)
os.chdir(cmd.split(' ')[1])   # 获取cd 命令的参数,交给os.chdir来切换目录
pass else:
data = subprocess.getoutput(cmd)  # 其他命令,交给subprocess处理,
data_length = len(data)
if data_length != 0:
pass
else:
pass

处理完相关的命令,将结果返会给客户端显示就可以了。最后吗客户端保存一个变量,用来保存当前的执行路径显示出来,就像[C:\Users\Tab\PycharmProjects\myftp\New_ftp\New_Server (help)]:这样。

五、总结

到这里,就基本将关键性的东西讲完了,其他的都是一些简单的操作,只要自己稍微注意一下,你就可以写出一个类似的东西。

  

  

用 Python实现一个ftp+CRT(不用ftplib)的更多相关文章

  1. python 开发一个支持多用户在线的FTP

    ### 作者介绍:* author:lzl### 博客地址:* http://www.cnblogs.com/lianzhilei/p/5813986.html### 功能实现 作业:开发一个支持多用 ...

  2. 利用Python制作一个只属于和她的聊天器,再也不用担心隐私泄露啦!

    ------------恢复内容开始------------ 是否担心微信的数据流会被监视?是否担心你和ta聊天的小秘密会被保存到某个数据库里?没关系,现在我们可以用Python做一个只属于你和ta的 ...

  3. 【Python学习 】Python实现的FTP上传和下载功能

    一.背景 最近公司的一些自动化操作需要使用Python来实现FTP的上传和下载功能.因此参考网上的例子,撸了一段代码来实现了该功能,下面做个记录. 二.ftplib介绍 Python中默认安装的ftp ...

  4. python下操作ftp上传

    生产情况:tomcat下业务log备份,目录分多级,然后对应目录格式放到ftp上:所以,结构上 我就是一级一级目录进行判断(因为我没有找到在ftp一次判断其子目录是否存在),还有一个low点就是我没有 ...

  5. python socket编程---从使用Python开发一个Socket示例说到开发者的思维和习惯问题

    今天主要说的是一个开发者的思维和习惯问题. 思维包括编程的思维和解决一个具体问题的分析思维,分析思路,分析方法,甚至是分析工具. 无论是好习惯还是不好的习惯,都是在者一天一天的思维中形成的.那些不好的 ...

  6. 一句python代码搭建FTP服务

    环境搭建: python windows/linux pip install pyftpdlib (安装失败请到这里下载:https://pypi.python.org/pypi/pyftpdlib/ ...

  7. 用python做一个搜索引擎(Pylucene)

    什么是搜索引擎? 搜索引擎是“对网络信息资源进行搜集整理并提供信息查询服务的系统,包括信息搜集.信息整理和用户查询三部分”.如图1是搜索引擎的一般结构,信息搜集模块从网络采集信息到网络信息库之中(一般 ...

  8. 基于线程开发一个FTP服务器

    一,项目题目:基于线程开发一个FTP服务器 二,项目要求: 基本要求: 1.用户加密认证   2.允许同时多用户登录   3.每个用户有自己的家目录 ,且只能访问自己的家目录   4.对用户进行磁盘配 ...

  9. web自动化 基于python+Selenium+PHP+Ftp实现的轻量级web自动化测试框架

    基于python+Selenium+PHP+Ftp实现的轻量级web自动化测试框架   by:授客 QQ:1033553122     博客:http://blog.sina.com.cn/ishou ...

随机推荐

  1. Java集合框架(list,Queue)

    List和Queue都继承自Collection接口 list常规用法 List判断两个对象相等的标准:equals方法返回true class A2 { public boolean equals( ...

  2. 2017 ACM-ICPC 西安网络赛 F.Trig Function Chebyshev多项式

    自己太菜,数学基础太差,这场比赛做的很糟糕.本来想吐槽出题人怎么都出很数学的题,现在回过头来想还是因为自己太垃圾,竞赛就是要多了解点东西. 找$f(cos(x))=cos(nx)$中$x^m$的系数模 ...

  3. aidl.exe'' finished with non-zero exit value 1问题解决【转载】

    PS:Android Studio用AIDL时,碰到一个非常棘手的问题,但是百度之,压根非法解决,FQ出去,终于找到了一篇解决问题的文章,特地转载之. 之前使用aidl传递的都是基本的数据类型比如in ...

  4. 搭建简单的node+express+mongodb项目

    安装 首先要确保已经安装了 Node.js,接下来创建一个目录,然后进入此目录并将其作为当前工作目录. mkdir myapp cd myapp 通过 npm init 命令为应用创建一个 packa ...

  5. 如何发布一个自定义Node.js模块到NPM(详细步骤,附Git使用方法)

    咱们闲话不多说,直接开始! 由于我从没有使用过MAC,所以我不保证本文中介绍的操作与MAC一致. 文章开始我先假定各位已经在window全局安装了Node.js,下面开始进行详细步骤介绍: 本文本着, ...

  6. jQuery代码优化:基本事件

    jQuery对事件系统的抽象与优化也是它的一大特色.本文仅从事件系统入手,简要分析一下jQuery为什么提供mouseenter和mouseleave事件,它们与标准的mouseover.mouseo ...

  7. CentOS7防火墙fiewall用法

    CentOS7与以前常用的CentOS6还是有一些不同之处的,比如在设置开放端口的时候稍许有些不同,常用的iptables命令已经被 firewalld代替.这几天正好有在CentOS7系统中玩Sea ...

  8. 36 - 网络编程-TCP编程

    目录 1 概述 2 TCP/IP协议基础 3 TCP编程 3.1 通信流程 3.2 构建服务端 3.3 构建客户端 3.4 常用方法 3.4.1 makefile方法 3.5 socket交互 3.4 ...

  9. ACM International Collegiate Programming Contest World Finals 2013

    ACM International Collegiate Programming Contest World Finals 2013 A - Self-Assembly 题目描述:给出\(n\)个正方 ...

  10. ubuntu中安装软件包问题 ------有一些软件包无法被安装。如果您用的是 unstable 发行版。。。

    在ubuntu中安装软件包提示 有一些软件包无法被安装.如果您用的是 unstable 发行版,这也许是因为系统无法达到您要求的状态造成的.该版本中可能会有一些您需要的软件包尚未被创建或是它们已被从新 ...