一、作业需求:

使用SELECT或SELECTORS模块实现并发简单版FTP

允许多用户并发上传下载文件

二、readme

一、作业需求:

使用SELECT或SELECTORS模块实现并发简单版FTP

允许多用户并发上传下载文件

二、博客地址:http://www.cnblogs.com/catepython/p/8973372.html

三、运行环境

操作系统:Win10

Python:3.6.4rcl

Pycharm:2017.3.4

四、功能实现

1)实现所有基本需求

2)充分利用了面向对象式编程

3)实现了单线程多并发上传/下载文件(多路复用IO)模式

五、测试

1)文件名为空判断

2)指令格式化判断

3)文件名/用户目录有效判断

六、备注

1、服务端put()函数中一遇到 data = conn.recv(size) 逻辑
就会出现“BlockingIOError:无法立即完成一个非阻止性套接字操作”报错 注:尝试过异常处理但效果不明显
完美解决办法:客户端直接把所需上传文件路径与操作字典{'action':'put','file':'e:\xx\xx\'}
一并发送至服务端。然后服务端直接读取路径并写入server目录中,这样就避免了“BlockingIOError”异常报错

readme

三、流程图

四、目录结构图

五、核心代码

bin目录程序运行文件

# -*- coding:utf-8 -*-
# Author:D.Gray
from core import socket_client
start = socket_client.MyClient()

client_start.py

# -*- coding:utf-8 -*-
# Author:D.Gray
from core import socket_server
start = socket_server.MyServer()
start.start()

server_start.py

conf配置文件目录

# -*- coding:utf-8 -*-
# Author:D.Gray
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) LOCAL_HOST = ('localhost',6969) HOME_PATH = os.path.join(BASE_DIR,'File','home_file') SERVER_PATH = os.path.join(BASE_DIR,'File','server_file')

setting.py

core主逻辑程序目录

# -*- coding:utf-8 -*-
# Author:D.Gray
import selectors
import socket,json,os,hashlib,sys,time
from conf import setting class MyClient(object):
def __init__(self):
self.client = socket.socket()
self.client.connect(setting.LOCAL_HOST)
self.run = self.run() def run(self):
'''
启动函数
:return:
'''
while True:
help = '''\033[33;1m
"get":"用于下载文件,例如:get readme.txt 即 get 文件名"
"put":"用于上传文件,例如:put readme.txt 即 put 文件名"
\033[0m'''
print(help)
meg = input('root@selectors_client>>>:').strip().split()
if len(meg) == 0:continue
if len(meg) >= 2:
dic = {
'action':meg[0],
'filename':meg[1],
'filesize':0
}
if hasattr(self,str(dic['action'])):
action = getattr(self,str(dic['action']))
action(dic)
else:
print('\033[31;1m请输入有效操作指令\033[0m')
else:
print('\033[31;1m请输入有效操作指令\033[0m') def put(self,*args):
'''
上传文件至服务端函数
:param args:
:return:
'''
args = args[0] # args = {'action':put,'filename':xxx}
#print('in the put:',args)
file_path = os.path.join(setting.HOME_PATH,args['filename'])
if os.path.isfile(file_path):
file_totle_size = os.stat(file_path).st_size #获取文件大小
args['filesize'] = file_totle_size #在字典中增加 文件大小键值对
self.client.send(json.dumps(args).encode()) #字典序列化上传
print('\033[34;1m发送文件相关信息至服务端:\n%s\033[0m'%args)
recv_server = self.client.recv(1024) #接收服务端回调:允许客户端上传文件参数
if recv_server.decode() == '':
print('\033[35;1m收到服务回调信息:%s\033[0m'%recv_server.decode())
print('开始上传文件...')
i = 0
file_totle_size = int(file_totle_size)
send_size = 0
self.m = hashlib.md5()
with open(file_path,'rb') as f:
while send_size < file_totle_size:
if file_totle_size - send_size < 1024:
size = file_totle_size - send_size
data = f.read(size)
send_size += len(data)
else:
size = 1024
data = f.read(size)
send_size += len(data)
self.client.send(data)
'''
进度条
'''
str1 = '已上传 %s Bytes'%send_size
str2 = '%s%s'%(round((send_size/file_totle_size)*100,2),'%')
str3 = '[%s%s]'%('*'*i,str2)
sys.stdout.write('\033[32;1m\r%s%s\033[0m'%(str1,str3))
sys.stdout.flush()
while i<=50:
i += 2
break
time.sleep(1)
'''
加密认证
'''
self.m.update(data)
self.encryption()
else:
print('\033[31;1m收到服务端异常回调信息\033[0m',recv_server.decode())
else:
print('\033[31;1m未找到该文件\033[0m') def get(self,*args):
dic = args[0]
self.client.send(json.dumps(dic).encode())
server_recv = self.client.recv(1024) #获取服务端回调参数或文件大小
if server_recv.decode() != '':
print('收到服务端发送过来的文件大小[%s bytes]'%server_recv.decode())
file_path = os.path.join(setting.HOME_PATH,dic['filename'])
print('开始接受文件')
file_totle_size = int(server_recv.decode())
self.m = hashlib.md5()
recv_size = 0
i = 0
with open(file_path,'wb') as f:
while recv_size < file_totle_size:
if file_totle_size - recv_size < 1024:
size = file_totle_size - recv_size
else:
size = 1024
data = self.client.recv(size)
self.m.update(data)
f.write(data)
recv_size += len(data)
'''
进度条
'''
str1 = '已下载 %s Bytes' % recv_size
str2 = '%s%s' % (round((recv_size / file_totle_size) * 100, 2), '%')
str3 = '[%s%s]' % ('*' * i, str2)
sys.stdout.write('\033[32;1m\r%s%s\033[0m' % (str1, str3))
sys.stdout.flush()
i += 2
time.sleep(1)
'''
加密认证
'''
self.encryption()
else:
print('服务端未找到该文件') def encryption(self):
'''
文件加密函数
:return:
'''
enc = input('\n文件已上传是否需要加密(n取消加密)>>>:')
if enc == 'n':
self.client.recv(1024) #因服务端无论客户端是否选择加密都会发送加密消息过来,所以这里也必须接受下防止粘包
print('已取消加密文件上传成功')
else:
file_md5 = self.m.hexdigest()
server_md5 = self.client.recv(1024) #接受服务端文件加密信息
print("\033[32;1m本地文件加密:%s\n服务端文件加密:%s\033[0m" % (file_md5 ,server_md5.decode()))
if file_md5 == server_md5.decode():
print("\033[32;1m加密认证成功\033[0m")
else:
print("加密认证失败")

socket_client

# -*- coding:utf-8 -*-
# Author:D.Gray
import selectors
import socket,os,json,errno,hashlib,time
from conf import setting
sel = selectors.DefaultSelector() class MyServer(object):
def __init__(self):
self.server = socket.socket() def start(self):
'''
启动函数
:return:
'''
print('等待链接...')
self.server.bind(setting.LOCAL_HOST)
self.server.listen(100)
self.server.setblocking(False)
self.register() def register(self):
'''
注册函数
:return:
'''
sel.register(self.server,selectors.EVENT_READ,self.accept)
while True:
events = sel.select()
for k,mask in events:
callback = k.data
callback(k.fileobj,mask) def accept(self,server,mask):
'''
服务器监听函数
:param server:
:param mask:
:return:
'''
conn,self.addr = server.accept()
print('\033[32;1m已和客户端[%s]建立了链接\033[0m'%(conn))
conn.setblocking(False)
sel.register(conn,selectors.EVENT_READ,self.read) def read(self,conn,mask):
'''
接收客户端信息函数
:param conn:
:param mask:
:return:
'''
data = conn.recv(1024)
#print('in the read conn:',conn)
if data:
action_dic = json.loads(data) #序列化data={'action':xxx}
print('\033[35;1m收到客户端操作指令:%s\033[0m'%action_dic['action'])
if hasattr(self,str(action_dic['action'])):
action = getattr(self,str(action_dic['action']))
action(action_dic,conn) #此时传参一定要用conn,千万不能传self.conn 多并发时会出现异常
else:
print('\033[31;1m客户端已断开\033[0m')
conn.unregister(conn) #关闭客户端链接
conn.close() def put(self,*args):
'''
服务端接收客户端文件函数
:param args:
:return:
'''
conn = args[1] #客户端链接地址
dic = args[0] #操作字典
file_path = os.path.join(setting.SERVER_PATH,dic['filename'])
print('\033[34;1m已收到客户端传来的文件相关信息:\n%s\033[0m'%dic)
conn.send(b'')
print('\033[35;1m发送回调给客户端:100\033[0m')
print('开始接收客户端文件')
self.m = hashlib.md5()
with open(file_path,'wb') as f:
with open(dic['file'],'rb')as fr:
line = fr.read()
f.write(line)
self.m.update(line)
self.encryption(conn) def get(self,*args):
'''
服务端上传文件至客户端函数
:param args:
:return:
'''
conn = args[1]
dic = args[0]
print('in the get:',dic)
file_path = os.path.join(setting.SERVER_PATH,dic['filename'])
if os.path.isfile(file_path):
file_totle_size = os.stat(file_path).st_size
conn.send(str(file_totle_size).encode())
print('开始发送文件给客户端')
self.m = hashlib.md5()
with open(file_path,'rb') as f:
for line in f:
self.m.update(line)
conn.send(line)
self.encryption(conn)
else:
conn.send(b'')
print('未找到该文件') def encryption(self,*args):
'''
加密函数
:param args:
:return:
'''
conn = args[0]
server_md5 = self.m.hexdigest()
conn.send(str(server_md5).encode())
print('文件操作完成并发送加密信息【%s】至客户端' % server_md5)

socket_server

老男孩Day11作业:selectors版socket的更多相关文章

  1. selectors版socket

    一.作业需求: 使用SELECT或SELECTORS模块实现并发简单版FTP 允许多用户并发上传下载文件 二.readme 一.作业需求: 使用SELECT或SELECTORS模块实现并发简单版FTP ...

  2. Python selectors实现socket并发

    selectors模块 此模块允许基于选择模块原语构建高级别和高效的I / O多路复用. 鼓励用户使用此模块,除非他们想要精确控制使用的os级别的原语. 注:selectors也是包装了select高 ...

  3. 老男孩Day12作业:RabbitMQ-RPC版主机管理程序

    一.作业需求 1.可以对指定机器异步的执行多个命令 例子: 请输入操作指令>>>:run ipconfig --host 127.0.0.0 in the call     tack ...

  4. 老男孩Day16作业:登录、注册、后台管理页面(动态)

    一.作业需求: 1.后台管理主界面(左边菜单框.(全选.反选)框.返回顶部按钮) 2.老男孩登录.注册页面 二.博客地址:https://www.cnblogs.com/catepython/p/93 ...

  5. 老男孩Day14作业:堡垒机

    一.作业需求: 1.业务需求 兼顾业务安全目标与用户体验,堡垒机部署后,不应使用户访问业务系统的访问变的复杂,否则工作将很难推进,因为没人喜欢改变现状,尤其是改变后生活变得更艰难     保证堡垒机稳 ...

  6. 老男孩Day9作业:高级FTP

    一.作业需求 1. 用户加密认证(已完成) 2. 多用户同时登陆(已完成) 3. 每个用户有自己的家目录且只能访问自己的家目录(已完成) 4. 对用户进行磁盘配额.不同用户配额可不同(已完成) 5.  ...

  7. 老男孩Day8作业:FTP

    1.作业需求 开发简单的FTP: 1. 用户登陆 2. 上传/下载文件 3. 不同用户家目录不同 4. 查看当前目录下文件 5. 充分使用面向对象知识 2.流程图 3.目录结构 4.代码区 bin目录 ...

  8. 老男孩Day7作业:选课系统

    1.作业需求:角色:学校.学员.课程.讲师 1. 创建北京.上海 2 所学校 2. 创建linux , python , go 3个课程 , linux\py 在北京开, go 在上海开 3. 课程包 ...

  9. C#版 Socket编程(最简单的Socket通信功能)

    示例程序是同步套接字程序,功能很简单,只是客户端发给服务器一条信息,服务器向客户端返回一条信息:这里只是一个简单的示例,是一个最基本的socket编程流程,在接下来的文章中,会依次记录套接字的同步和异 ...

随机推荐

  1. keil的使用:新建Project

    新建项目--->新建文件夹----->把新建的项目放在自己的文件夹中------>选择开发板------>添加开发板的驱动文件---->main函数 项目分组基本如图,S ...

  2. HDFS的介绍

    设计思想 分而治之:将大文件.大批量文件,分布式存放在大量服务器上,以便于采取分而治之的方式对海量数据进行运算分析: 在大数据系统中作用:为各类分布式运算框架(如:mapreduce,spark,te ...

  3. 【知识碎片】 Linuxb 篇

    3.登录mysql 开启MySQL服务后,使用MySQL命令可以登录.一般使用mysql -uroot -p即可.如果数据库不是本机,则需要加参数,常用参数如下:1,-h,指定ip地址,默认为loca ...

  4. Apache Hive 简介及安装

    简介 Hive 是基于 Hadoop 的一个数据仓库工具,可以将结构化的数据文件 映射为一张数据库表,并提供类 SQL 查询功能. 本质是将 SQL 转换为 MapReduce 程序. 主要用途:用来 ...

  5. Spring总结十:事务案例

    数据库表Account: 导包: <dependencies> <!--测试--> <dependency> <groupId>junit</gr ...

  6. 求输出和为n的所有连续自然数序列

    这是编程之美中的一道题.编程之美中的题目是这样的: 1+2=3 4+5=9 2+3+4=9 等式的左边都是两个或者两个以上的连续自然数相加,那么是不是所有的整数都可以写成这样的形式? 问题1:写个程序 ...

  7. 【总结整理】面试pm常见的问题---摘自《人人都是产品经理》

    求职路上,“怼”来“怼”去的面试问题 人人都是产品经理社区 发布于 2018-10-29 19:53:06 举报 阅读数:1418 ​​在求职路上,面对那些被“怼”到过的面试问题,应该如何处理? 一个 ...

  8. cout/cin

    转载来源:http://baike.baidu.com/link?url=NiNaSw0pF7RqFO8u0jx8KWk9yOfFFYy24xCJlQ6_qMcw5_WBzRKOqsO6tfvvJbZ ...

  9. 数据库MySQL 之 库操作

    数据库MySQL 之 库操作 浏览目录 系统数据库简介 数据库操作 命名规范 用户权限 修改密码 忘记密码 中文乱码问题 一.系统数据库简介 information_schema :虚拟库,不占用磁盘 ...

  10. JMS-消息中间件的应用02-安装ActiveMQ-来自慕课学习-新手学习

    What is ActiveMQ?       -----突然好想打英文,好奇怪 请看来自官网的介绍: Apache ActiveMQ ™ is the most popular and powerf ...