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服务端, ...
随机推荐
- E. New Reform_贪心,深搜,广搜。
E. New Reform time limit per test 1 second memory limit per test 256 megabytes input standard input ...
- php5.3 yum安装升级版本到 php5.6
centOS系统下如何将php升级到5.6,之前通过yum来安装lamp环境,直接升级的话,提示没有更新包,也就是说默认情况下php5.3.3是最新 1.查看已经安装的php版本号 键入下面代码: ...
- Yarn下Map数控制
public List<InputSplit> getSplits(JobContext job) throws IOException { long minSize = Math.max ...
- 第23章 I2C—读写EEPROM—零死角玩转STM32-F429系列
第23章 I2C—读写EEPROM 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/f ...
- Search in Rotated Sorted Array——LeetCode
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. (i.e. ...
- Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-9enuqi/MySQL-python/
hu@hu-VirtualBox:/home/newdisk/telnet-scanner$ sudo pip install MySQL-python[sudo] hu 的密码: The direc ...
- 【Java】对象、类(抽象类与内部类)、接口
博文内容概况 对象和类 抽象类 接口 内部类 对象和类 对象是对客观事物的抽象,类是对对象的抽象.类是一种数据类型,其外观和行为由用户定义.类中可以设置两种类型的元素:字段(有时被称为数据成员)和方法 ...
- ES6学习(三):数组的扩展
chapter08 数组的扩展 8.1 扩展运算符 8.1.1 扩展运算符的含义 ... 如同rest运算符的逆运算,将一个数组转换为用逗号分隔的参数序列. console.log(...[1, 2, ...
- UVa中国麻将(Chinese Mahjong,Uva 11210)
简单的回溯题 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm ...
- (转)为什么国外 MMORPG 中不采用自动寻路等功能?
不只是自动寻路,现在网游中的教学引导系统,辅助系统的功能强大程度,友好程度都可以说到了变态的程度,开发这些功能投入的资源甚至要超过游戏内容本身.究其原因,还是竞争越来越激烈,人心越来越浮躁,游戏商家为 ...