一、作业需求:

使用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. 爬虫入门到放弃系列02:html网页如何解析

    前言 上一篇文章讲了爬虫的概念,本篇文章主要来讲述一下如何来解析爬虫请求的网页内容. 一个简单的爬虫程序主要分为两个部分,请求部分和解析部分.请求部分基本一行代码就可以搞定,所以主要来讲述一下解析部分 ...

  2. Docker下使用centos无法使用systemctl怎么办

    提交正在使用的容器: docker commit [ContainerId] 提交停止正在运行无法使用Systemctl的容器: docker stop [ContainerId] 删除这个容器(可选 ...

  3. 手动合并hadoop namenode editlog

    一. 基本概念 1.NN恢复实际上是由fsimage开始(这个相当于数据的base),如果有多个fsimage,会自动选择最大的fsimage,然后按照editlog序列日志开始执行日志 2.seen ...

  4. Educational Codeforces Round 39

    Educational Codeforces Round 39  D. Timetable 令\(dp[i][j]\)表示前\(i\)天逃课了\(j\)节课的情况下,在学校的最少时间 转移就是枚举第\ ...

  5. Codeforces Round #673 (Div. 2) A. Copy-paste(贪心)

    题目链接:https://codeforces.com/contest/1417/problem/A 题意 给出一个大小为 $n$ 的数组 $a$,每次操作可以选择两个数,然后将一个数加到另一个数上, ...

  6. 矩阵树定理(Kirchhoff || Laplace)初探——Part 1(无向图计数)

    必备知识: 高斯消元,图论基本知识(好像就这...(雾)) 这里是无向图部分,请不要走错场... 定义 我们将邻接矩阵定义为矩阵A(u,v),我想邻接矩阵就不用再多说了: 我们将每个点的度数矩阵定义为 ...

  7. 计蒜客-A1139 dfs

    在一个 n \times mn×m 的方格地图上,某些方格上放置着炸弹.手动引爆一个炸弹以后,炸弹会把炸弹所在的行和列上的所有炸弹引爆,被引爆的炸弹又能引爆其他炸弹,这样连锁下去. 现在为了引爆地图上 ...

  8. Codeforces Round #496 (Div. 3) D. Polycarp and Div 3 (数论)

    题意:给你一个巨长无比的数,你可以将这个数划成任意多个部分,求这些部分中最多有多少个能被\(3\)整除. 题解:首先我们遍历累加每个位置的数字,如果某一位数或者累加和能被\(3\)整除(基础知识,不会 ...

  9. 力扣992.K个不同整数的子数组-C语言实现

    题目 原题链接 给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续.不一定独立的子数组为好子数组. (例如,[1,2,3,1,2] 中有 3 个不同的整数: ...

  10. docker-理论题01

    1.什么是docker?答:docker是开源的应用容器引擎:开发人员把他们的应用及依赖包打包发布到容器当中. 2.docker和VMware的区别? 答:docker是半解耦,VMware是解耦:d ...