程序文件结构

具体代码实现

服务端

执行文件bin/ftb_server

import os,sys
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #os.path.dirname()取文件的上一级目录路径
sys.path.append(PATH) #导入的路径不在同一文件夹下,需要自己添加环境变量 from core import main if __name__ == "__main__":
main.ArgvHandler()

配置文件conf/setting

import os,sys
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) IP = "127.0.0.1"
PORT = 8080 ACCOUNT_PATH = os.path.join(BASE_DIR,"conf","accounts.cfg.txt")

主程序文件core/main

import os,sys

import optparse         #optparse模块用来给脚本传递命令参数,并用预定设置好的选项来解析传过来的参数
import socketserver
from conf import setting
from core import server class ArgvHandler:
def __init__(self):
self.op = optparse.OptionParser() #实例化
# self.op.add_option("-s","--server",dest="server")
# self.op.add_option("-P","--port",dest="port") #用于增加设定输入的内容对应的关系
options,args = self.op.parse_args()
# print(options) #将上面增加的设定做键值对应后封装成一个对象
# print(options.server) #调用这个对象中的属性
# print(args) #当输入的的值超出add_option的设置,把这些值放在一个列表中 self.verify_args(options,args) def verify_args(self,options,args):
cmd = args[0] if hasattr(self,cmd):
func = getattr(self,cmd)
func() def start(self):
print("the server is working..")
s = socketserver.ThreadingTCPServer((setting.IP,setting.PORT),server.ServerHandler)
s.serve_forever()

服务端程序core/server

import socketserver
import json
import configparser
from conf import setting
import os 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 True:
data = self.request.recv(1024).strip()
data = json.loads(data.decode("utf-8"))
"""
{"action":"auth",
"usename":"ss",
"pwd":123
}
"""
if data.get("action"): #对接收的内容进行验证
if hasattr(self,data.get("action")):
func = getattr(self,data.get("action")) #验证通过执行action键对应的函数
func(**data)
else:
print("Invalid cmd")
else:
print("not") def send_response(self,state_code):
response = {"status_code":state_code} #将状态码回传给客户端
self.request.sendall(json.dumps(response).encode("utf-8")) def auth(self,**data):
username = data["username"]
password = data["password"] user = self.authenticate(username,password)
if user: #将客户端传输的数据与数据库中的内容匹配判断,根据结果回传状态码
self.send_response(254)
else:
self.send_response(253) def authenticate(self,user,pwd):
cfg = configparser.ConfigParser() #调用数据库
cfg.read(setting.ACCOUNT_PATH) #读取数据库中的内容 if user in cfg.sections():
if cfg[user]["password"] == pwd:
self.user = user #将得到的用户名赋值给self.user,这样其他的函数也可以用这个用户名
self.mainPath = os.path.join(setting.BASE_DIR,"home",self.user) #将用户的路径拼接出来
return user #具体操作命令
def put(self,**data):
print(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 == "Y": #选择续传
self.request.sendall(str(file_has_size).encode(("utf-8"))) #回传已有多少字节
has_received += file_has_size #将已有字节长度赋值给初始值
f = open(abs_path,"ab") #再在已有基础上写入
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") while has_received < file_size: #判断接收到数据是否小于传输过来的文件大小,小于则继续传输
try:
data = self.request.recv(1024)
except Exception:
break
f.write(data)
has_received += len(data)
f.close() def ls(self,**data):
file_list = os.listdir(self.mainPath) #列出当前目录下的所有文件
file_str = "\n".join(file_list)
if not len(file_list):
file_str = "<empty dir>"
self.request.sendall(file_str.encode("utf-8")) def cd(self,**data):
dirname = data.get("dirname")
if dirname == "..":
self.mainPath = os.path.dirname(self.mainPath)
self.mainPath = os.path.join(self.mainPath,dirname)
self.request.sendall(self.mainPath.encode("utf-8")) def makdir(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"))

客户端:

主程序ftb_client

import socket
import optparse
import configparser
import json
import os
import sys 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 ClientHandler():
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.make_connection()
self.mainPath = os.path.dirname(os.path.abspath(__file__))
self.last = 0 def verify_args(self,options,args):
server = options.server
port = options.port
username = options.username
password = options.password if int(port)<65535:
return True
else:
exit("the port is in 0-65535") def make_connection(self): #客户端套接字流程
self.sock = socket.socket()
self.sock.connect((self.options.server,int(self.options.port))) def interactive(self):
if self.authenticate(): #判断是否验证成功
print("begin to interactive...")
while True:
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 put(self,*cmd_list):
action,local_path,target_path = cmd_list
local_path = os.path.join(self.mainPath,local_path) #将上传的文件的本地路径与自定义的mainPath目录拼接 file_name = os.path.basename(local_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.send(json.dumps(data).encode("utf-8")) is_exist = self.sock.recv(1024).decode("utf-8") #接收服务端返回的消息 has_sent = 0
if is_exist == "": #文件不完整
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_exist == "": #文件已存在
return
else:
pass f = open(local_path,"rb")
f.seek(has_sent) #将光标移动到多少个字节处
while has_sent < file_size: #文件传输
data = f.read(1024)
self.sock.send(data)
has_sent += len(data)
self.show_progress(has_sent,file_size)
f.close() def show_progress(self,has,total):
rate = float(has)/float(total)
rate_num = int(rate * 100)
if self.last != rate_num:
sys.stdout.write("%s%% %s\r" %(rate_num,"#"*rate_num))
self.last = 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):
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) def mkdir(self,*cmd_list):
data = {
"action":"mkdir",
"dirname":cmd_list[1]
}
self.sock.send(json.dumps(data).encode("utf-8"))
self.sock.recv(1024).decode("utf-8") def authenticate(self): #判断是否输入,再进行传输
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) #接收的状态码是字典格式{"status_code":state_code}
return data def get_auth_result(self,user,pwd): #将数据传输到服务端
data = {
"action":"auth",
"username":user,
"password":pwd
}
self.sock.send(json.dumps(data).encode("utf-8"))
response = self.response()
print(response["status_code"])
if response["status_code"] == 254: #根据回传的状态码判断是否验证正确
self.user = user
self.current_dir = user
print(STATUS_CODE[254])
return True #验证成功返回
else:
print(STATUS_CODE[response["status_code"]]) ch = ClientHandler() ch.interactive()

用Python编写一个ftb的更多相关文章

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

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

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

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

  3. Python 编写一个有道翻译的 workflow 教程

    最近使用有道翻译的 workflow 总是翻译不了,可能是 appKey 失效了或者超过调用上限,所以打算自己实现一个. 创建 workflow 打开 Alfred3 的 Preferences,选择 ...

  4. 如何用Python编写一个聊天室

    一.课程介绍 1.简介 本次项目课是实现简单聊天室程序的服务器端和客户端. 2.知识点 服务器端涉及到asyncore.asynchat和socket这几个模块,客户端用到了telnetlib.wx. ...

  5. 为Python编写一个简单的C语言扩展模块

    最近在看pytorh方面的东西,不得不承认现在这个东西比较火,有些小好奇,下载了代码发现其中计算部分基本都是C++写的,这真是要我对这个所谓Python语音编写的框架或者说是库感觉到一丢丢的小失落,细 ...

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

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

  7. Python编写一个Python脚本

    我想要一个可以为我的所有重要文件创建备份的程序.(下面测试环境为python2.7) 1.backup_ver1.py #!/usr/bin/python import os import time ...

  8. day-1 用python编写一个简易的FTP服务器

    从某宝上购买了一份<Python神经网络深度学习>课程,按照视频教程,用python语言,写了一个简易的FTP服务端和客户端程序,以前也用C++写过聊天程序,编程思路差不多,但是pytho ...

  9. 用python编写一个计算器

    # 1 - 2 * ((60-30 +(-40.0/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2)))# 通过Pyt ...

随机推荐

  1. 测试常用shell命令

    正则表达式 特殊字符 $ . ' * [ ] ^ | ( ) \ + ? awk使用心得 将抽取域在屏幕上显示和保存到文件中 awk '{print $3}' t1.txt | tee file1.t ...

  2. python之新式类与经典类

    经典类与新式类 经典类:P 或 P()--深度查找,向上查父节点 新式类 :P(object)---广度查找,继承object,新式类的方法较多  

  3. kubernetes配置dashborad,web界面

    一,将kubernetes-dashboard.yaml-1.10和admin-rbac.yaml和token.sh的上传到k8s的计算机上 .如图 二,切入到这三个文件所在的目录下,执行命令:kub ...

  4. RestTemplate 调用本地服务 connection refused

    当需要使用服务间的互相调用的时候,通常来说最优雅的方式莫过于Feign调用了.但是有时候特殊原因还是需要使用httpClient之类的工具. 本次我在使用RestTemplate调用本地服务的时候,会 ...

  5. mysq远程连接报错,host..

    在本机登入mysql后,更改"mysql"数据库里的"user"表里的"host"项,从"localhost"改为'%' ...

  6. rosbag record and play

    话题录制: 录制所有发布出来的话题,此时默认将话题保存在一个以当时时间戳命名的文件夹中:   $ rosbag record -a1 录制指定话题:   $ rosbag record /topic1 ...

  7. HAProxy & Keepalived L4-L7 高可用负载均衡解决方案

    目录 文章目录 目录 HAProxy 负载均衡器 应用特性 性能优势 会话保持 健康检查 配置文件 负载均衡策略 ACL 规则 Web 监控平台 Keepalived 虚拟路由器 核心组件 VRRP ...

  8. Java多线程学习——任务定时调度

    Timer 本身就是一个线程,最主要的方法就是schedule(). schedule()的参数介绍: schedule(TimerTask task, long delay) //延迟delay毫秒 ...

  9. spark MLlib矩阵四则运算,线性代数

    1.导包请看我的上一篇博文,maven项目的包 https://www.cnblogs.com/wuzaipei/p/10965680.html 2.denseMatirx 矩阵四则运算如下 版本不同 ...

  10. [LeetCode] 1092. Shortest Common Supersequence

    LeetCode刷题记录 传送门 Description Given two strings str1 and str2, return the shortest string that has bo ...