SELECTORS模块实现并发简单版FTP
环境:windows, python 3.5
功能:
使用SELECTORS模块实现并发简单版FTP
允许多用户并发上传下载文件 结构:
ftp_client ---|
bin ---|
start_client.py ......启动客户端
conf---|
config.py ......客户端参数配置
system.ini ......客户端参数配置文件
core---|
clients.py ......客户端主程序
home ......默认下载路径
ftp_server ---|
bin ---|
start_server.py ......启动服务端
conf---|
config.py ......服务端参数配置
system.ini ......服务端参数配置文件
core---|
servers.py ......服务端主程序
db ---|
data.py ......存取用户数据
home ......默认上传路径 功能实现:
客户端输入命令,根据命令通过映射进入相应方法,上传或者下载;
服务端用SELECTORS模块实现并发,接收数据,通过action用映射进入相应方法; 如何使用:
启动start_server.py,启动start_client.py;
在客户端输入get pathname/put pathname进行上传下载文件,开启多个客户端可并发上传下载。 core:
#!/usr/bin/env python
# -*-coding:utf-8-*-
# Author:zh
import socket
import os
import json
import random
import conf
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+os.sep+"home"+os.sep # 默认下载存放路径为当前路径的home目录下 class FtpClient(object):
def __init__(self):
self.client = socket.socket() def connect(self, ip, port):
self.client.connect((ip, port)) def interactive(self):
# 入口
while True:
cmd = input(">>").strip()
if len(cmd) == 0:
continue
cmd_list = cmd.split()
if len(cmd_list) == 2:
cmd_str = cmd_list[0]
if hasattr(self, cmd_str):
func = getattr(self, cmd_str)
func(cmd)
else:
print("输入格式错误")
self.help()
else:
print("输入格式错误")
self.help() @staticmethod
def help():
msg = '''
get filepath(下载文件)
put filepath(上传文件)
'''
print(msg) def get(self, *args):
# 下载
data = args[0].split()
action = data[0]
path = data[1]
send_msg = {
"action": action, # 第一次请求,需要服务端返回文件信息
"file_path": path # F:\oracle课程\(必读)11gOCP考试流程与须知.rar
}
self.client.send(json.dumps(send_msg).encode())
data = self.client.recv(1024)
data = json.loads(data.decode())
if data["sign"] == "":
file_length = data["length"]
file_path = PATH+data["filename"]
if os.path.isfile(file_path):
file_path = file_path + str(random.randint(1,1000))
file = open(file_path, "wb")
send_msg_2 = {
"action": "get_file_data", # 第二次请求,请求客户端给发送文件数据
"file_path": path # 第二次请求依然需要传入文件路径,也可以在服务端利用全局变量保存
}
self.client.send(json.dumps(send_msg_2).encode())
get_length = 0
while get_length < int(file_length):
data = self.client.recv(1024)
file.write(data)
get_length += len(data)
else:
file.close()
if get_length == int(file_length):
print("下载成功")
else:
print("文件传输失败")
os.remove(file_path) # 如果传输过程出现问题,删除文件
else:
print("文件不存在") def put(self, *args):
# 上传
data = args[0].split()
action = data[0]
file_path = data[1]
if os.path.isfile(file_path):
msg = {
"action": action, # 上传第一次发送,发送文件大小和姓名
"length": os.stat(file_path).st_size,
"filename": file_path[file_path.rfind("\\") + 1:]
}
self.client.send(json.dumps(msg).encode())
self.client.recv(1024) # 避免黏包
file = open(file_path, "rb")
for line in file:
self.client.send(line) # 上传第二次发送,发送文件数据,没有action,在服务端通过try,json报错的进入接收数据的方法里
else:
file.close()
sign = self.client.recv(1024)
if sign == b'':
print("上传成功")
else:
print("上传失败")
else:
print("文件不存在") def run():
data = conf.config.Configuration()
conf_data = data.get_config()
obj = FtpClient()
obj.connect(conf_data[0][1], int(conf_data[1][1]))
obj.interactive()
clients
core:
#!/usr/bin/env python
# -*-coding:utf-8-*-
# Author:zh
import selectors
import socket
import json
import os
import random
import conf
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+os.sep+"home"+os.sep # 默认上传存放路径为当前路径的home目录下 class SelectorFtp(object): def __init__(self):
self.sel = selectors.DefaultSelector() def connect(self, ip, port):
sock = socket.socket()
sock.bind((ip, port))
sock.listen(100)
sock.setblocking(False)
self.sel.register(sock, selectors.EVENT_READ, self.accept)
while True:
events = self.sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask) def accept(self, sock, mask):
conn, addr = sock.accept() # Should be ready
conn.setblocking(False)
self.sel.register(conn, selectors.EVENT_READ, self.interactive) def interactive(self, conn, mask):
# 回调函数,每次接收消息都判断action,根据action分别调用不同函数信息交互
try:
data = conn.recv(1024)
if data:
try:
cmd_dict = json.loads(data.decode())
action = cmd_dict['action']
if hasattr(self, action):
func = getattr(self, action)
func(cmd_dict, conn)
except (UnicodeDecodeError, json.decoder.JSONDecodeError, TypeError) as e:
# put发送文件数据,无法通过json打包,采取json报错后进入方法循环接收
self.put_file_data(data, conn)
else:
self.sel.unregister(conn)
conn.close()
except ConnectionResetError as e:
print(e)
# 客户端意外断开时注销链接
self.sel.unregister(conn)
conn.close() def get(self, *args):
# 下载第一步:判断文件是否存在并返回文件大小和文件名
conn = args[1]
file_path = args[0]["file_path"]
if os.path.isfile(file_path):
sign = ""
length = os.stat(file_path).st_size
else:
sign = ""
length = None
msg = {
"sign": sign,
"length": length,
"filename": file_path[file_path.rfind("\\")+1:]
}
conn.send(json.dumps(msg).encode()) def get_file_data(self, *args):
# 下载第二步,传送文件数据
conn = args[1]
file_path = args[0]["file_path"]
file = open(file_path, "rb")
for line in file:
conn.send(line)
else:
file.close() def put(self, *args):
# 上传第一步,接收文件大小和文件名
conn = args[1]
self.length = args[0]["length"]
self.filename = PATH + args[0]["filename"]
if os.path.isfile(self.filename):
self.filename = self.filename + str(random.randint(1, 1000))
self.file = open(self.filename, "wb")
self.recv_size = 0
conn.send(b"") # 避免客户端黏包 def put_file_data(self, *args):
# json报错后进入此循环接收数据
conn = args[1]
self.recv_size += len(args[0])
self.file.write(args[0])
if int(self.length) == self.recv_size:
self.file.close()
conn.send(b"") def run():
data = conf.config.Configuration()
conf_data = data.get_config()
obj = SelectorFtp()
obj.connect(conf_data[0][1], int(conf_data[1][1]))
servers
config:
#!/usr/bin/env python
# -*-coding:utf-8-*-
# _author_=zh
import os
import configparser
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) class Configuration(object):
def __init__(self):
self.config = configparser.ConfigParser()
self.name = PATH+os.sep+"conf"+os.sep+"system.ini" def init_config(self):
# 初始化配置文件,ip :客户端IP,port:客户端端口
if not os.path.exists(self.name):
self.config["config"] = {"ip": "localhost", "port": 1234}
self.config.write(open(self.name, "w", encoding="utf-8", )) def get_config(self, head="config"):
'''
获取配置文件数据
:param head: 配置文件的section,默认取初始化文件config的数据
:return:返回head中的所有数据(列表)
'''
self.init_config() # 取文件数据之前生成配置文件
self.config.read(self.name, encoding="utf-8")
if self.config.has_section(head):
section = self.config.sections()
return self.config.items(section[0])
config
SELECTORS模块实现并发简单版FTP的更多相关文章
- 基于selectors模块实现并发的FTP
import socketimport os,sysBASE_DIR = os.path.dirname(os.path.abspath(__file__)) class selectFtpClien ...
- SELECT版FTP
功能: 1.使用SELECT或SELECTORS模块实现并发简单版FTP 2.允许多用户并发上传下载文件环境: python 3.5特性: select 实现并发效果运行: get 文件名 #从服务器 ...
- day33 网络编程之线程,并发以及selectors模块io多路复用
io多路复用 selectors模块 概要: 并发编程需要掌握的知识点: 开启进程/线程 生产者消费者模型!!! GIL全局解释器锁(进程与线程的区别和应用场景) 进程池线程池 IO模型(理论) 1 ...
- IO模型《七》selectors模块
一 了解select,poll,epoll IO复用:为了解释这个名词,首先来理解下复用这个概念,复用也就是共用的意思,这样理解还是有些抽象, 为此,咱们来理解下复用在通信领域的使用,在通信领域中为了 ...
- Go语言之进阶篇简单版并发服务器
1.简单版并发服务器 示例1: package main import ( "fmt" "net" "strings" ) //处理用户请求 ...
- 并发编程之IO模型比较和Selectors模块
主要内容: 一.IO模型比较分析 二.selectors模块 1️⃣ IO模型比较分析 1.前情回顾: 上一小节中,我们已经分别介绍过了IO模型的四个模块,那么我想大多数都会和我一样好奇, 阻塞IO和 ...
- python 全栈开发,Day44(IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)
昨日内容回顾 协程实际上是一个线程,执行了多个任务,遇到IO就切换 切换,可以使用yield,greenlet 遇到IO gevent: 检测到IO,能够使用greenlet实现自动切换,规避了IO阻 ...
- {python之IO多路复用} IO模型介绍 阻塞IO(blocking IO) 非阻塞IO(non-blocking IO) 多路复用IO(IO multiplexing) 异步IO(Asynchronous I/O) IO模型比较分析 selectors模块
python之IO多路复用 阅读目录 一 IO模型介绍 二 阻塞IO(blocking IO) 三 非阻塞IO(non-blocking IO) 四 多路复用IO(IO multiplexing) 五 ...
- 验证客户端的链接合法性和socketserver模块实现并发
本节内容: 1.验证客户端的链接合法性 2.socketserver模块实现并发 一.验证客户端的链接合法性 首先,我们来探讨一下,什么叫验证合法性, 举个例子:有一天,我开了一个socket服务端, ...
随机推荐
- Breaking Biscuits(模板题-求凸边形的宽)
Breaking Biscuits 时间限制: 1 Sec 内存限制: 128 MB Special Judge提交: 70 解决: 26[提交] [状态] [讨论版] [命题人:admin] ...
- redis redis的连接
昨天2017年12月26日,我刚刚从网上下载了redis.经过一天的摸索,踩了不少坑.昨天下午,比较磕磕巴巴,今天早上 终于比较完善地完成了一次小操作. 使用cmd的重要步骤 1.输入redis-se ...
- EF中 实现延迟加载 lazyload
1.创建数据库 2.利用数据库 生成视图 生成2个实体类 和一个model1类 3.写代码 (1) 创建 上下文对象 (2) (3)查询结果 注释: 延迟加载的原因,因为我们操作数据库不会那么简单, ...
- linux flushing file system caches
We may drop the file system caches on Linux to free up memory for applications. Kernels 2.6.16 and n ...
- phpMyAdmin提示找不到mcrypt和mbstring模块
yum install php-mcryptyum install php-mbstringphp -m 查看是否安装成功 service httpd restart 重启服务器 注: 这里可能会出现 ...
- JS无限添加HTML到指定位置
用JS把HTML添加到指定位置有两种写法,一种是用字符串,一种是用javascript中的方法 第一种: 用字符串写 <h2>利用JS无限添加一个相同部分</h2> <h ...
- deep learning书的阅读
最近坚持读书,虽然大多数读的都是一些闲书,传记.历史或者散文之类的书籍,但是也读了点专业书.闲书是散时间读的,放车里,有时间就拿起来读读,专业书则更多的靠得是专注.因为我给自己的规定是一定时间内读完几 ...
- BZOJ2005: [Noi2010]能量采集(容斥原理 莫比乌斯反演)
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 4727 Solved: 2877[Submit][Status][Discuss] Descript ...
- finddler的安装与设置
这是抓取手机包的设置 过滤 新安装的,可能还需要证书问题
- 怎么退出jQuery的each函数
返回 'false' 将停止循环 (就像在普通的循环中使用 'break').返回 'true' 跳至下一个循环(就像在普通的循环中使用'continue'). 以下举例如何退出 each 函数和退出 ...