一、作业需求:

使用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. Unity3d开发中遇到的问题记录

    安装Unity unity官方提供免费的community版本,功能健全,下载时还有Visual Studio,非常方便. 官方文档 Unity的官方文档非常权威!详尽!可靠!很多关于函数细节的疑惑都 ...

  2. Java安全之jar包调试技巧

    Java安全之jar包调试技巧 调试程序 首先还是创建一个工程,将jar包导入进来 调试模式的参数 启动中需要加入特定参数才能使用debug模式,并且需要开放调试端口 JDK5-8: -agentli ...

  3. Failed to initialize policy for cpu: 0

    今天在使用vmware安装ubuntu16.04的时候出现下列错误: Failed to initialize policy for cpu: 0 (-19),刚开始还以为是镜像文件出现了问题,结果发 ...

  4. hdu 6832 A Very Easy Graph Problem 构造树+dfs

    题意: 给你一个n个点m条边的图,对于第i条边,它的长度是2i,对于每一个顶点,它不是0类型,就是1类型.你需要找出来对于所有的"两个不同类型的点之间最短距离"的和 题解(参考:h ...

  5. hdu2546 饭卡

    Problem Description 电子科大本部食堂的饭卡有一种很诡异的设计,即在购买之前判断余额.如果购买一个商品之前,卡上的剩余金额大于或等于5元,就一定可以购买成功(即使购买后卡上余额为负) ...

  6. B - 来找一找吧 HihoCoder - 1701

    题目: 这次到渣渣问桶桶了... 准备给你n个数a1, a2, ... an,桶桶你能从中找出m个特别的整数吗,我想让任意两个之差都是k的倍数. 请你计算有多少种不同的选法.由于选法可能非常多,你只需 ...

  7. F(x) HDU - 4734

    题意: 给你一个n位的数x(A(n)A(n-1)...A(1)),那么F(x)=A(n)*2^(n-1)+A(n-1)*2^(n-2)......+A(1)*2^(0) 题目输入A.B 你需要找出来在 ...

  8. 加密后的程序在Win8中无法运行

    需要使用dotNET_Reactor 4.7以上的版本加密后才能在Win8中使用.

  9. VS2010下创建MVC4项目注意事项

    1.安装VS SP1. 2.安装NuGet Package Manager. (1)打开VS2010,进入"工具--扩展管理器". (2)点击"联机库",等待搜 ...

  10. iPhone 如何查看 Wi-Fi 密码

    iPhone 如何查看 Wi-Fi 密码 shit, 需要安装第三方软件 refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问! 原创文 ...