一、作业需求:

使用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() == '100':
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() != '204':
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'100')
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'204')
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)

student_view

 
 
 

selectors版socket的更多相关文章

  1. 老男孩Day11作业:selectors版socket

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

  2. Python selectors实现socket并发

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

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

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

  4. Python学习之UDP版socket&SocketServer

    7.6 基于UDP的socket 无连接的,不必与对方建立连接,而是直接把数据发送给对方: 适用于一次传输销量数据结构,可靠性不高的应用环境,因为其传输速率快 # 服务端 import socket ...

  5. SELECTORS模块实现并发简单版FTP

    环境:windows, python 3.5功能:使用SELECTORS模块实现并发简单版FTP允许多用户并发上传下载文件 结构:ftp_client ---| bin ---| start_clie ...

  6. python笔记-10(socket提升、paramiko、线程、进程、协程、同步IO、异步IO)

    一.socket提升 1.熟悉socket.socket()中的省略部分 socket.socket(AF.INET,socket.SOCK_STREAM) 2.send与recv发送大文件时对于黏包 ...

  7. Python开发【第八章】:Socket

    一.Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. so ...

  8. selectors实现高并发

    1. 下面的例子,客户端给服务端发送消息,服务端把消息返回 server #!/usr/bin/env python import selectors import socket import tim ...

  9. 基于 socket.io, 简单实现多平台类似你猜我画 socket 数据传输

    一.前言 socket.io 实现了实时双向的基于事件的通讯机制,是基于 webSocket 的封装,但它不仅仅包括 webSocket,还对轮询(Polling)机制以及其它的实时通信方式封装成了通 ...

随机推荐

  1. Codeforces Round #675 (Div. 2)【ABCD】

    比赛链接:https://codeforces.com/contest/1422 A. Fence 题意 给出三条边 $a,b,c$,构造第四条边使得四者可以围成一个四边形. 题解 $d = max( ...

  2. HDU 2897 邂逅明下(巴士变形)

    题意: 给你n个石子,你最少取p个,最多取q个,问谁能赢 题解: 变形版的巴什博弈,当n>=q+1的时候,那么还是以q+1为一组拿走,剩下一个(n%(q+1)),这个时候如果它小于p的话都直接输 ...

  3. Codeforces Round #555 (Div. 3) C2. Increasing Subsequence (hard version) (贪心)

    题意:给你一组数,每次可以选队首或队尾的数放入栈中,栈中元素必须保持严格单增,问栈中最多能有多少元素,并输出选择情况. 题解:首先考虑队首和队尾元素不相等的情况,如果两个数都大于栈顶元素,那么我们选小 ...

  4. Codeforces Round #345 (Div. 1) A. Watchmen (数学,map)

    题意:给你\(n\)个点,求这\(n\)个点中,曼哈顿距离和欧几里得距离相等的点对数. 题解: 不难发现,当两个点的曼哈顿距离等于欧几里得距离的时候它们的横坐标或者纵坐标至少有一个相同,可以在纸上画一 ...

  5. Codeforces Round #656 (Div. 3) B. Restore the Permutation by Merger (模拟)

    题意:有两个完全相同的排列,将其中一个的元素按相对顺序插入另外一个排列中,给你操作完的排列,求原排列. 题解:感觉看看样例就能直接写了啊,直接遍历,用桶存数字个数,如果桶为空,直接输出即可. 代码: ...

  6. SpringCloud @RefreshScope标注的Bean在生命周期中怎么样的?

    @RefreshScope 前言 该文章是由上一篇引申而来的,上一篇文章描述了Nacos是怎么触发配置刷新的,接着来写有关@RefreshScope的一些东西,都是由DEBUG而来 上一篇 文章概览 ...

  7. Raven1渗透实战

    Raven1渗透实战 目录: 1.wordpress爆破用户 2.wp-config得到数据库账号密码 3.ssh连接4.pythn提权(sudo python -c 'import pty;pty. ...

  8. Ubuntu 18.04 + pip3 install virtualenvwrapper 找不到virtualenvwrapper.sh

    Reference Ubuntu 18.04 只自带python3.6.5, 因此不想装python2了, 但通过apt install 装virtualenvwrapper时发现必须得装python ...

  9. webpack 5 new features All In One

    webpack 5 new features All In One Webpack 5 release (2020-10-10) https://webpack.js.org/blog/2020-10 ...

  10. 最新 Markdown for GitHub教程

    Markdown 教程 Markdown 是什么? Markdown 是一种方便记忆.书写的纯文本标记语言,用户可以使用这些标记符号以最小的输入代价生成极富表现力的文档:譬如您正在阅读的这份文档. 它 ...