从某宝上购买了一份《Python神经网络深度学习》课程,按照视频教程,用python语言,写了一个简易的FTP服务端和客户端程序,以前也用C++写过聊天程序,编程思路差不多,但是python编程时更顺畅,代码量更少。没有很高深的理论知识,也不需要扎实的编程基础,知道需要用哪些库就行了。

  两种语言对比,初次感受到python语言的易用之处,python的核心是简洁清晰,也是伪代码的最佳实践语言。可以理解为C语言的一些常用功能库的转义,所以普遍认为python语言带来编程便捷的同时,也会带来一部分性能的牺牲。但这并不也是绝对的,有时候程序的开发效率更为重要,更何况并不一定人人都是大牛,用C或者其它语言未必就能写出比python更高性能的代码。整体学习完以后,还会再来深入对比下python和其他语言的差异。

1、基本功能

  登陆验证

  上传:支持断点上传,MD5验证

  简单命令:cd、ls、mkdir

2、常用模块

  下面是我汇总的用到的模块信息,要写出完整的代码,首先得了解基本的python函数和库,例如:

  os,sys库对目录和文件的处理;

  socket和socketserver对网络通信的处理;

  json对传输数据格式的处理;

  configparse对配置文件的处理;

  hashlib对MD5校验的处理;

  optparse对命令行输入参数的格式化处理等;

  另外还需要了解如何利用反射机制,处理多If else分支的函数调用等。

  关于python反射机制,参考链接:https://www.cnblogs.com/Guido-admirers/p/6206212.html

3、具体代码

  

  ftp_client.py:FTP客户端主程序

import socket
import optparse
import json
import os,sys
import hashlib STATUS_CODE = {
250 : "Invalid cmd format, e.g: {'action':'get','filename':'test.py','size':344}",
251 : "Invalid cmd ",
252 : "Invalid auth data",
253 : "Wrong username or password",
254 : "Passed authentication",
255 : "Filename doesn't provided",
256 : "File doesn't exist on server",
257 : "ready to send file",
258 : "md5 verification",
800 : "the file exist,but not enough ,is continue? ",
801 : "the file exist !",
802 : " ready to receive datas",
900 : "md5 valdate success"
} sk = socket.socket()
sk.connect(("127.0.0.1",18000)) class ClientHander():
def __init__(self): #初始化变量
self.op = optparse.OptionParser()
self.op.add_option("-s","--server",dest="server")
self.op.add_option("-P", "--port", dest="port")
self.op.add_option("-u", "--username", dest="username")
self.op.add_option("-p", "--password", dest="password")
self.options,self.args = self.op.parse_args()
#self.verify_args(self.options,self.args)
self.mainPath = os.path.dirname(os.path.abspath(__file__))
self.last = 0
self.is_authenticated = False
self.is_connected = False
self.is_needed_md5 = True #开始建立连接
self.make_connection() # 验证用户输入信息
def verify_args(self,options,args):
server = options.server
port = options.port
username = options.username
password = options.password
if int(port) > 0 and int(port) < 65535:
return True
else:
exit("the port should been in 0-65535") # 连接服务器
def make_connection(self):
if not self.is_connected:
self.sock = socket.socket()
self.sock.connect(("127.0.0.1", 18000))
self.is_connected = True
return def interactive(self): # 判断是否已经验证过
if not self.is_authenticated:
self.make_connection()
self.authenticate() # 开始和服务器进行交互
cmd_info = input("[%s]"%self.current_dir).strip()
# 利用反射机制,建立命令与功能对应关系
cmd_list = cmd_info.split()
if hasattr(self,cmd_list[0]):
func = getattr(self,cmd_list[0])
func(*cmd_list) def authenticate(self):
if not self.is_connected:
print("没有连接FTP服务器")
return
if self.options.username is None or self.options.password is None:
username = input("username:")
password = input("password:")
return self.get_auth_result(username,password)
return self.get_auth_result(self.options.username,self.options.password) def response(self):
data = self.sock.recv(1024).decode("utf-8")
data = json.loads(data)
return data def get_auth_result(self,username,password):
data = {
"action":"auth",
"username":username,
"password":password
}
self.sock.send(json.dumps(data).encode("utf-8"))
res = self.response()
print("res",res["status_code"])
if res["status_code"] == 254:
self.username = username
self.current_dir = username
self.is_authenticated = True
print(STATUS_CODE[254])
return True
else:
print(STATUS_CODE[res["status_code"]]) def put(self,*cmd_list): # 1 发送上传命令
action,local_path,target_path = cmd_list
local_path = os.path.join(self.mainPath,local_path)
file_name = os.path.basename(local_path)
file_size = os.stat(local_path).st_size
data = {
"action":"put",
"file_name": file_name,
"file_size": file_size,
"target_path": target_path
}
self.sock.sendall(json.dumps(data).encode("utf-8")) # 2 接收服务器检查结果
has_sent = 0
is_exsit = self.sock.recv(1024).decode("utf-8") # 3 根据服务器检查结果,执行相应命令
# 文件存在但不完整
if is_exsit == "":
print("服务器文件已经存在,且不完整")
# 和服务器确认是否继续上传
choice = input("The file exist but not enough,is continue?[Y/N]").strip()
# 继续上传
if choice.upper() == "Y":
self.sock.sendall("Y".encode("utf-8"))
# 等待服务器返回存在文件大小
continue_position = self.sock.recv(1024).decode("utf-8")
has_sent += int(continue_position)
# 不继续上传
else:
self.sock.sendall("N".encode("utf-8"))
# 文件存在且完整
elif is_exsit == "":
print("服务器文件已经存在,且完整")
return
f = open(local_path,"rb")
f.seek(has_sent) md5_obj = None
if self.is_needed_md5:
md5_obj = hashlib.md5() while has_sent < file_size:
data = f.read(1024)
self.sock.sendall(data)
if md5_obj:
md5_obj.update(data)
has_sent += len(data) mdt_val_server = self.sock.recv(1024).decode("utf-8")
mdt_val_client = md5_obj.hexdigest()
self.show_progress(has_sent,file_size)
print("client's md5 is %s"%mdt_val_client)
print("server's md5 is %s" %mdt_val_server)
f.close() if mdt_val_client == mdt_val_server:
print("put success")
else:
print("Athenticate MD5 failed.") def show_progress(self,has,total):
rate = float(has) / float(total)
rate_num = int(rate*100)
print("%s%% %s\r"%(rate_num,"#"*rate_num)) def ls(self,*cmd_list):
data = {
"action":"ls"
}
self.sock.sendall(json.dumps(data).encode("utf-8"))
data = self.sock.recv(1024).decode("utf-8")
print(data) def cd(self,*cmd_list):
# cd images
data = {
"action":"cd",
"dirname":cmd_list[1]
}
self.sock.sendall(json.dumps(data).encode("utf-8"))
data = self.sock.recv(1024).decode("utf-8")
self.current_dir = os.path.basename(data)
print(os.path.basename(data)) def mkdir(self,*cmd_list):
data = {
"action":"mkdir",
"dirname":cmd_list[1]
} self.sock.sendall(json.dumps(data).encode("utf-8"))
data = self.sock.recv(1024).decode("utf-8")
print(data) ch = ClientHander()
while 1:
ch.interactive()

  ftp_server.py:FTP服务器端主程序

import os,sys

PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(PATH) from core import main if __name__ == '__main__':
main.ArgvHandler()

  main.py:处理参数输入

import optparse
import socketserver
from conf import settings
from core import server class ArgvHandler(): def __init__(self):
self.op = optparse.OptionParser()
#self.op.add_option("-s","--server",dest = "server")
#self.op.addd_option("-P", "--port", dest="port") options,args = self.op.parse_args()
self.verify_args(options,args) def verify_args(self,options,args):
#首先判断参数的长度
if len(args) < 1:
print("参数个数太少,请重新输入.")
else:
cmd = args[0]
if hasattr(self,cmd):
func = getattr(self,cmd)
func()
self.start() def start(self):
s = socketserver.ThreadingTCPServer((settings.IP,settings.PORT),server.ServerHandler)
print("服务器已经启动")
s.serve_forever() def help(self):
pass

  server.py:具体代码实现

import socketserver
import json
import configparser
from conf import settings
import os
import hashlib STATUS_CODE = { 250 : "Invalid cmd format, e.g: {'action':'get','filename':'test.py','size':344}",
251 : "Invalid cmd ",
252 : "Invalid auth data",
253 : "Wrong username or password",
254 : "Passed authentication",
255 : "Filename doesn't provided",
256 : "File doesn't exist on server",
257 : "ready to send file",
258 : "md5 verification", 800 : "the file exist,but not enough ,is continue? ",
801 : "the file exist !",
802 : " ready to receive datas", 900 : "md5 valdate success" } class ServerHandler(socketserver.BaseRequestHandler):
def handle(self):
while 1:
data = self.request.recv(1024).strip()
if not data:continue
data = json.loads(data.decode("utf-8")) if data.get("action"):
if hasattr(self,data.get("action")):
func = getattr(self,data.get("action"))
func(**data)
else:
print("Invalid cmd")
else:
print("Invalid cmd") def send_response(self,status_code):
response = {"status_code":status_code}
self.request.sendall(json.dumps(response).encode("utf8")) def auth(self,**data):
username = data["username"]
password = data["password"]
print("收到客户端验证请求,用户名:",username,"密码:",password)
user = self.authenticate(username,password)
if user:
self.send_response(254)
else:
self.send_response(253) def authenticate(self,username,password):
cfg = configparser.ConfigParser()
cfg.read(settings.ACCOUNT_PATH)
print("正在读取配置用户配置信息:",settings.ACCOUNT_PATH) if username in cfg.sections():
if cfg[username]["Password"] == password:
self.useranme = username
self.mainPath = os.path.join(settings.BASE_DIR,"home",self.useranme)
print("成功匹配上用户信息,用户密码:", cfg[username]["Password"])
print("用户根目录为:",self.mainPath)
return username def put(self,**data):
print("data",data)
file_name = data.get("file_name")
file_size = data.get("file_size")
target_path = data.get("target_path") abs_path = os.path.join(self.mainPath,target_path,file_name) has_received = 0 if os.path.exists(abs_path):
file_has_size = os.stat(abs_path).st_size
if file_has_size < file_size:
#断点续传
self.request.sendall("".encode("utf-8"))
choice = self.request.recv(1024).decode("utf-8")
if choice.upper() == "Y":
self.request.sendall(str(file_has_size).encode("utf-8"))
f = open(abs_path,"ab")
has_received += file_has_size
else:
f = open(abs_path,"wb")
else:
#文件完全存在
self.request.sendall("".encode("utf-8"))
return
else:
self.request.sendall("".encode("utf-8"))
f = open(abs_path, "wb") md5_obj = hashlib.md5() while has_received < file_size:
try:
data = self.request.recv(1024)
f.write(data)
md5_obj.update(data)
has_received += len(data)
except Exception as e:
print("传输文件发生异常:",str(e))
break # 给客户端发送MD5
md5_val = md5_obj.hexdigest()
self.request.sendall(md5_val.encode("utf-8")) f.close() def ls(self,**data):
file_list = os.listdir(self.mainPath)
print("ls %s"%self.mainPath)
if not file_list:
file_str = "<empty dir>"
else:
file_str = "\n".join(file_list)
self.request.sendall(file_str.encode("utf-8")) def cd(self,**data):
dir_name = data.get("dirname")
print("cd %s\n"%dir_name)
if dir_name == "..":
self.mainPath = os.path.dirname(self.mainPath)
else:
self.mainPath = os.path.join(self.mainPath,dir_name) self.request.sendall(self.mainPath.encode("utf-8")) def mkdir(self,**data):
dirname = data.get("dirname")
path = os.path.join(self.mainPath,dirname)
if not os.path.exists(path):
if "/" in dirname:
os.makedirs(path)
else:
os.mkdir(path)
self.request.sendall("create success".encode("utf-8"))
else:
self.request.sendall("dirname exist".encode("utf-8"))

  accounts.cfg:账户信息,登陆验证用

[DEFAULT]
[tanbiao]
Password = tanbiao
Quotation = 100 [root]
Password = root
Quotation = 100

  setting.py:配置信息

import os

BASE_DIR=os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

IP="127.0.0.1"
PORT=18000 ACCOUNT_PATH=os.path.join(BASE_DIR,"conf","accounts.cfg")

day-1 用python编写一个简易的FTP服务器的更多相关文章

  1. 用python编写一个合格的ftp程序,思路是怎样的?

      经验1.一般在比较正规的类中的构造函数.都会有一个verify_args函数,用于验证传入参数.尤其是对于系统传参.2.并且系统传参,其实后面大概都是一个函数名 例如:python server. ...

  2. 使用Python创建一个简易的Web Server

    Python 2.x中自带了SimpleHTTPServer模块,到Python3.x中,该模块被合并到了http.server模块中.使用该模块,可以快速创建一个简易的Web服务器. 我们在C:\U ...

  3. 用Python编写一个简单的Http Server

    用Python编写一个简单的Http Server Python内置了支持HTTP协议的模块,我们可以用来开发单机版功能较少的Web服务器.Python支持该功能的实现模块是BaseFTTPServe ...

  4. 基于OpenGL编写一个简易的2D渲染框架-06 编写一个粒子系统

    在这篇文章中,我将详细说明如何编写一个简易的粒子系统. 粒子系统可以模拟许多效果,下图便是这次的粒子系统的显示效果.为了方便演示,就弄成了一个动图. 图中,同时显示了 7 种不同粒子效果,看上去效果挺 ...

  5. 使用 python 编写一个授权登录验证的模块

    使用 python 编写一个授权登录验证的模块 我们编写的思路: 1.登录的逻辑:如果用户名和密码正确,就返回 token . 2.生成 token 的逻辑,根据用户名,随机数,当前时间 + 2 小时 ...

  6. C#编写一个简易的文件管理器

    编写一个简易的文件管理器,通过本次实验,练习 TreeView.ListView 和SplitContainer 控件的使用,同时熟悉 C#文件系统的操作方法以及 File 类和 Directory类 ...

  7. C 实现一个简易的Http服务器 (二)

    正文 - 直接搞起 C 实现一个简易的Http服务器 很久以前写过一个简易的http服务器, 后面和一个朋友交流, 反思后发现问题不少.在这里简单搞一下. 让其更加简单去表现httpd本质, 弱化协议 ...

  8. 用 Python 快速实现 HTTP 和 FTP 服务器

      用 Python 快速实现 HTTP 服务器 有时你需临时搭建一个简单的 Web Server,但你又不想去安装 Apache.Nginx 等这类功能较复杂的 HTTP 服务程序时.这时可以使用  ...

  9. 快速搭建一个本地的FTP服务器

    快速搭建一个本地的FTP服务器   如果需要开发FTP文件上传下载功能,那么需要在本机上搭建一个本地FTP服务器,方便调试. 第一步:配置IIS Web服务器 1.1 控制面板中找到"程序& ...

随机推荐

  1. [.NET]使用十年股价对比各种序列化技术

    1. 前言 上一家公司有搞股票,当时很任性地直接从服务器读取一个股票10年份的股价(还有各种指标)在客户端的图表上显示,而且因为是桌面客户端,传输的数据也是简单粗暴地使用Soap序列化.获取报价的接口 ...

  2. css导航条等元素位置不变

    在容器元素中插入 position: fixed; 如果是在微信小程序中,直接用bottom或者top等就可以简单的设置导航条了.

  3. Django入门-基本数据库API

    # 现在系统里还没有 Question 对象 >>> Question.objects.all() <QuerySet []> # 创建新 Question # 在 se ...

  4. mysql执行计划简介

    介绍 本篇主要通过汇总网上的大牛的知识,简单介绍一下如何使用mysql的执行计划,并根据执行计划判断如何优化和是否索引最优. 执行计划可显示估计查询语句执行计划,从中可以分析查询的执行情况是否最优,有 ...

  5. 创建Maven项目时提示web.xml is missing and <failOnMissingWebXml> is set to true错误解决方案

    1. 右键点击Deployment Descriptor 2. 选择Generate Deployment Descriptor Stub P.S.下面顺便提一个小技巧: 创建动态web时先右键项目, ...

  6. Problem : 1012 ( u Calculate e )

    /*tips:本题只有输入,没有输出,在线测试只检测结果,所以将前面几个结果罗列出来就OK了.为了格式输出问题纠结了半天,最后答案竟然还是错的....所以啊,做题还是得灵活变通.*/ #include ...

  7. SIMD---SSE系列及效率对比

    SSE(即Streaming SIMD Extension),是对由MMX指令集引进的SIMD模型的扩展.我们知道MMX有两个明显的缺点: 只能操作整数. 不能与浮点数同时运行(MMX使用FPU寄存器 ...

  8. 微信公众号开发,weUi组件,问题总结

    1.实现上拉分页,Google兼容问题,weui框架提供的模板是这样的($(document.body).infinite()),在body定一个div通过这个div,实现上拉 $('#wrapper ...

  9. 【Python】 高级文件操作 shutil

    shutil 很多时候,我想要对文件进行重命名,删除,创建等操作的时候的想法就是用subprocess开一个子进程来处理,但是实际上shutil可以更加方便地提供os的文件操作接口,从而可以一条语句搞 ...

  10. Java多线程:死锁

    周末看到一个用jstack查看死锁的例子.昨天晚上总结了一下jstack(查看线程).jmap(查看内存)和jstat(性能分析)命令.供大家参考  1.Jstack 1.1 jstack能得到运行j ...