程序文件结构

具体代码实现

服务端

执行文件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. css命名和书写规范

    前言 在项目开发中对于css名字的命名和书写老是感觉很混乱,这对于代码的可读性以及维护提出了挑战,所以在闲暇之余看了一些这方面的内容,现总结如下... 1.命名规则说明 所有的命名最好都小写 属性的值 ...

  2. ubuntu下安装apatch

    在Ubuntu上安装Apache,有两种方式:1 使用开发包的打包服务,例如使用apt-get命令:2 从源码构建Apache.本文章将详细描述这两种不同的安装方式. 方法一:使用开发包的打包服务—— ...

  3. 快速找到oracle的alert日志

    https://jingyan.baidu.com/article/f3ad7d0fe5d31309c3345b9b.html

  4. 多网卡情况下接收udp组播

    多网卡下接收udp组播 往往会接收失败 因为用错了网卡 例如我想要接收2网段 其他电脑出的udp组播  我电脑有有线网和wifi在window下可以这样 route add 230.0.0.1 mas ...

  5. 八、RF的内置变量

    1.表示“空”的变量 ${EMPTY} 空 适用输入空的案例 2.表示“空格”的变量 ${SPACE} 空格,如果是需要5个空格可以这样写${SPACE*5} 3.目录的绝对路径 ${CURDIR} ...

  6. 用U盘完成win10系统的安装

    电脑太卡了,每次都要重装,然后每次忘记要从哪里开始动手,都要百度,仅以此篇记录下 目录 1.系统盘准备 2.从U盘启动安装 1.系统盘准备 第一步:在电脑中完成系统盘制作工具的安装,由于它是要依赖.n ...

  7. 中国MOOC_零基础学Java语言_第4周 循环控制

    4.1 for循环 Tips for loops 如果有固定次数,用for 如果必须执行一次,用do_while 其他情况用while 4.2 循环控制 break和continue 在循环前可以放一 ...

  8. 类Enum

    int compareTo(E o) 比较此枚举与指定对象的顺序. String name() 返回此枚举常量的名称,在其枚举声明中对其进行声明. int ordinal() 返回枚举常量的序数(它在 ...

  9. MyEclipse中的查找快捷键

    MyEclipse中的查找快捷键 1.Ctrl+H:可以搜索文件,Java类名.方法名.包名等等. 例如:在MyEclipse中打开Search弹出框,或者在菜单中打开Search弹出框, 定位到 F ...

  10. Spring 注解概览

    从Java5.0开始,Java开始支持注解.Spring做为Java生态中的领军框架,从2.5版本后也开始支持注解.相比起之前使用xml来配置Spring框架,使用注解提供了更多的控制Spring框架 ...