环境: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的更多相关文章

  1. 基于selectors模块实现并发的FTP

    import socketimport os,sysBASE_DIR = os.path.dirname(os.path.abspath(__file__)) class selectFtpClien ...

  2. SELECT版FTP

    功能: 1.使用SELECT或SELECTORS模块实现并发简单版FTP 2.允许多用户并发上传下载文件环境: python 3.5特性: select 实现并发效果运行: get 文件名 #从服务器 ...

  3. day33 网络编程之线程,并发以及selectors模块io多路复用

    io多路复用 selectors模块 概要: 并发编程需要掌握的知识点: 开启进程/线程 生产者消费者模型!!! GIL全局解释器锁(进程与线程的区别和应用场景) 进程池线程池 IO模型(理论) 1 ...

  4. IO模型《七》selectors模块

    一 了解select,poll,epoll IO复用:为了解释这个名词,首先来理解下复用这个概念,复用也就是共用的意思,这样理解还是有些抽象, 为此,咱们来理解下复用在通信领域的使用,在通信领域中为了 ...

  5. Go语言之进阶篇简单版并发服务器

    1.简单版并发服务器 示例1: package main import ( "fmt" "net" "strings" ) //处理用户请求 ...

  6. 并发编程之IO模型比较和Selectors模块

    主要内容: 一.IO模型比较分析 二.selectors模块 1️⃣ IO模型比较分析 1.前情回顾: 上一小节中,我们已经分别介绍过了IO模型的四个模块,那么我想大多数都会和我一样好奇, 阻塞IO和 ...

  7. python 全栈开发,Day44(IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)

    昨日内容回顾 协程实际上是一个线程,执行了多个任务,遇到IO就切换 切换,可以使用yield,greenlet 遇到IO gevent: 检测到IO,能够使用greenlet实现自动切换,规避了IO阻 ...

  8. {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) 五 ...

  9. 验证客户端的链接合法性和socketserver模块实现并发

    本节内容: 1.验证客户端的链接合法性 2.socketserver模块实现并发 一.验证客户端的链接合法性 首先,我们来探讨一下,什么叫验证合法性, 举个例子:有一天,我开了一个socket服务端, ...

随机推荐

  1. @RequestMapping,@ResponseBody,@RequestBody用法

    本文转载:http://blog.csdn.net/ff906317011/article/details/78552426 1.@RequestMapping 国际惯例先介绍什么是@RequestM ...

  2. 1.4 NBU配置备份策略(Policy)

    1.4 配置备份策略(Policy) 一个备份策略由四部分组成. Attributes(属性) Policy是否Active Policy类型 由此Policy产生的任务的优先级 使用的Storage ...

  3. C++之string基本字符系列容器

    string基本字符系列容器 C语言只提供了一个插入类型用来处理字符, 而对于字符串, 只能通过字符串数组来处理, 显得十分不便. C++STL提供了string基本字符系列容器来处理字符串, 可以把 ...

  4. ios各层

    数据持久层.业务逻辑层.表示层 数据持久层: 持久化(Persistence)意思就是当你退出app的时候它还会存在. dao层:DAO (Data Access Object) 数据访问对象是一个面 ...

  5. Spring任务执行器(TaskExecutor)

    Spring任务执行器(TaskExecutor)    Spring通州任务执行器(TaskExecutor)来实现多线程和并发编程,使用ThreadPoolTaskExecutor可实现一个基于线 ...

  6. rbg的代码

    不得不赞rbg的代码,写的是真的好,各种异常都考虑到了,至少常见的异常没有了. 还有selective search的代码,也是很赞. 而edgebox的代码则不行啊,demo写的太死,而且代码里只能 ...

  7. socket传送二进制流的一些总结

    第一次实质性的接触socket通信方面的工作,所以遇到的问题还真不少,写篇博客记录一下,提升下记忆. 需求是通过私有协议进行二进制数据的传输,必须保证数据包不能被丢失,所以选择tcp的socket进行 ...

  8. python导包语句执行

    今天在做项目中遇到一个问题,在first_page中引用login的登录方法,第一次执行登录可以正常登录,登录成功后,再选择返回主菜单,回到上个页面,再选择登录时报错“login_class isno ...

  9. 谷歌浏览器如何调试JS

    平常在开发过程中,经常会接触到前端页面.那么对于js的调试那可是家常便饭,谷歌浏览器是常用来调试JS代码的工具,本文主要介绍如何利用谷歌浏览器来调试JS代码,协助我们进行开发工作,加快开发效率. 1. ...

  10. vim编辑器使用习惯问题

    Ubuntu中vi在编辑状态下方向键不能用,一按方向键盘就出ABCD,想插入个字母还非常麻烦,还有回格键不能删除等我们平时习惯的一些键都不能使用. 解决办法: 可以安装vim full版本,在full ...