程序文件结构

具体代码实现

服务端

执行文件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. excel里面匹配部分字符的问题

                如上图所示,有两个表,前者存的是区号电话号连起来的电话号,后者存的是世界各地的区号,怎么把第一个表里分成两列,一列单独存区号,一列单独存电话号. ps:电话号码长度和区号长度都 ...

  2. eclipse导出java项目jar包(依赖第三方jar包)

    一.在项目根目录下建一个文件:MANIFEST.MF 内容: Manifest-Version: 1.0 Class-Path: lib/commons-compress-1.9.jar lib/co ...

  3. 1、安装Scrapy

    一.网址:https://doc.scrapy.org/en/latest/intro/install.html 二.安装过程中出现"cl.exe"找不到的错误,解决方法:http ...

  4. PropertyInfo、FieldInfo、MemberInfo的区别

    public class TestClass { ;//私有一律获取不到 public int b { ; } ; } } ; } public static void TestMethod() { ...

  5. 中国MOOC_面向对象程序设计——Java语言_期末考试编程题_1细胞自动机

    期末考试编程题 返回   这是期末考试的编程题 温馨提示: 1.本次考试属于Online Judge题目,提交后由系统即时判分. 2.学生可以在考试截止时间之前提交答案,系统将取其中的最高分作为最终成 ...

  6. es为什么要取消type? 或者为什么一个index下多个type会有问题

    同一个index下的不同的type下的相同的filed,在同一个index下其实会被认为是同一个filed. 否则,不同type中的相同字段名称就会在处理中出现冲突的情况,导致Lucene处理效率下降

  7. .net通用签名方法 webapi签名方法

    验证签名方法 [HttpGet] public HttpResponseMessage LockRegister(string 参数1, int 参数2, string 参数3, string 参数4 ...

  8. yum 下载rpm包 安装rpm包依赖关系

    方法一:yumdownloader 工具 1.安装工具包 yum install yum-utils -y 2.下载一个RPM包 yumdownloader <package-name> ...

  9. Python Requests post方法中data与json参数问题

    1.data参数 你想要发送一些编码为表单形式的数据——非常像一个 HTML 表单.要实现这个,只需简单地传递一个字典给 data 参数.你的数据字典在发出请求时会自动编码为表单形式,header默认 ...

  10. 【ABAP系列】SAP ABAP中ALV使用HTML的例子

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP ABAP中ALV使用HT ...