需求:

  1. 实现文件上传及下载功能
  2. 支持多连接并发传文件
  3. 使用select or selectors

流程图

import socket
import pickle
import sys
import time
import os A = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
class Ftp_client(object):
def __init__(self):
self.client = socket.socket()
def connet(self, ip, port):
'''
链接服务器
:param ip:
:param port:
:return:
'''
self.client.connect((ip, port))
name = self.main()
self.ftp_main(name)
def login(self):
'''
登录
:return:
'''
name = input('请输入姓名').lower().strip()
password = input('请输入密码')
dict = {
'attr': 'login',
'name': name,
'password': password,
}
self.client.sendall(pickle.dumps(dict))
data = self.client.recv(1024)
print(data.decode())
if data.decode()=='输入有误':
return False
else:
return name def register(self):
'''
注册
:return:
'''
name = input('请输入姓名').lower().strip()
pd = input('请输入密码')
dict = {'attr': 'register', 'name': name, 'password': pd}
self.client.sendall(pickle.dumps(dict))
data = self.client.recv(1024)
print(data.decode())
if data.decode() == '用户名已存在,请重新输入':
return False
else:
return name def main(self):
while True:
a = input('请输入 1. 用户登录 2. 用户注册 3.退出')
if a == '':
res = self.login()
elif a == '':
res = self.register()
elif a == '':
exit()
else:
print('输入有误')
continue
if res is False:
continue
else:
return res def download(self, name):
'''
下载
:return:
'''
filename = input('请输入下载文件名')
dic = {'attr': 'download', 'filename': filename, 'name': name}
self.client.sendall(pickle.dumps(dic))
size = self.client.recv(1024).decode()
if size == '该文件不存在':
print ('该文件不存在')
return False
else:
size = int(size)
try:
f = open(os.path.join(A, 'client','db', 'file', filename), 'xb') #文件不存在新建
except Exception:
f = open(os.path.join(A, 'client','db', 'file', filename), 'wb')#文件存在打开重新下载
if size == 0:
f.close()
print('接收完成')
else:
r_size = 0
while r_size < size:
file = self.client.recv(1024)
f.write(file)
r_size += len(file)
view_bar(r_size, size)
time.sleep(0.1)
else:
print('接收完成')
f.close() def upload(self, name):
filename = input('请输入上传的文件名')
if os.path.exists(os.path.join(A, 'client', 'db', 'file',filename)) and filename !='.':
size = os.path.getsize(os.path.join(A, 'client', 'db', 'file', filename)) #文件size
f = open(os.path.join(A, 'client', 'db', 'file', filename), 'rb')
else:
print ('此文件不存在')
return False
if size == 0:
dic = {'attr': 'upload', 'filename': filename, 'size': size, 'name': name}
self.client.sendall(pickle.dumps(dic))
else:
for line in f:
dic = {'attr': 'upload', 'filename': filename, 'size': size, 'name': name, 'text': line}
self.client.sendall(pickle.dumps(dic))
num = f.tell() #查看文件上传位置
view_bar(num, size)
time.sleep(0.1)
f.close()
print ('接收完成')
return False def ftp_main(self, name):
while True:
a = input('请输入相应的指令, download: 下载; upload: 上传; exit:退出').strip()
if hasattr(self, a):
func = getattr(self, a)
func(name)
elif a == 'exit':
exit()
else:
print('输入有误') def view_bar(num, total):
'''进度条'''
rate = float(num) / float(total)
rate_num = int(rate * 100)
r = '\r%d%%' % (rate_num, ) #\r 回到到开头
sys.stdout.write(r)
sys.stdout.flush() #删除记录 ftp = Ftp_client()
ftp.connet('localhost', 9999)

client

import selectors
import socket
import pickle
import os
A = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sel = selectors.DefaultSelector() def accept(sock,mask):
conn, addr = sock.accept()
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read) #调用read函数 def read(conn, mask):
try:
data = conn.recv(1024)
except Exception:
sel.unregister(conn) #出现异常,取消注册的conn
conn.close()
else:
if data:
r_dic = pickle.loads(data)
if r_dic['attr'] == 'login':
login(conn, r_dic)
elif r_dic['attr'] == 'register':
register(conn, r_dic)
elif r_dic['attr'] == 'download':
download(conn, r_dic)
elif r_dic['attr'] == 'upload': upload(r_dic)
else:
print('链接断开')
sel.unregister(conn)
conn.close() def login(conn, dict):
'''
登录
:param conn:
:param dict:
:return:
'''
db_dict = pickle.load(open(os.path.join(A, 'server', 'db', 'register'),'rb'))
if dict['name'] in db_dict:
if dict['password']==db_dict[dict['name']][0]:
conn.sendall('登录成功'.encode())
else:
conn.sendall('输入有误'.encode())
else:
conn.sendall('输入有误'.encode()) def register(conn, r_dic):
'''
注册
:param conn:
:param r_dic:
:return:
'''
if os.path.exists(os.path.join(A, 'db', r_dic['name'])):
conn.sendall('用户名已存在,请重新输入'.encode())
else:
conn.sendall('注册成功'.encode())
os.makedirs(os.path.join(A, 'server', 'db', r_dic['name']))
n_dict = pickle.load(open(os.path.join(A, 'server', 'db', 'register'), 'rb'))
n_dict[r_dic['name']] = r_dic['password']
pickle.dump(n_dict,open(os.path.join(A, 'server','db', 'register'), 'wb')) def upload(dic):
'''
下载
:param dic:
:return:
'''
print(dic)
f_size = int(dic['size']) #上传文件大小
print(f_size)
filename = dic['filename']
name = dic['name']
try:
f = open(os.path.join(A, 'server','db', name, filename), 'xb')
file_size = 0
print(f_size)
except Exception:
f = open(os.path.join(A, 'server','db', name, filename), 'ab')
file_size = os.path.getsize(os.path.join(A, 'server', 'db', name, filename))
if f_size == 0:
f.close()
return False
else:
if file_size< f_size:
f.write(dic['text'])
f.flush()
else:
f.close()
return False def download(conn, dic):
'''
下载
:param conn:
:param dic:
:return:
'''
l_path = os.path.join(A, 'server','db', dic['name'], dic['filename'])
if os.path.exists(l_path) and dic['filename']!= '.': #检查文件是否存在,文件名不能等于.
f = open(l_path, 'rb')
size = os.path.getsize(l_path) #检查文件
conn.sendall(str(size).encode()) #要以字符串格式传数字
for line in f:
conn.sendall(line)
f.close()
else:
conn.sendall('该文件不存在'.encode()) sock = socket.socket()
sock.bind(('localhost', 9999))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
while True:
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)

server

IO多路复用版FTP的更多相关文章

  1. python IO多路复用版FTP

    需求: 实现文件上传及下载功能 支持多连接并发传文件 使用select or selectors

  2. gevent协程、select IO多路复用、socketserver模块 改造多用户FTP程序例子

    原多线程版FTP程序:http://www.cnblogs.com/linzetong/p/8290378.html 只需要在原来的代码基础上稍作修改: 一.gevent协程版本 1. 导入geven ...

  3. 非阻塞套接字与IO多路复用(转,python实现版)

    非阻塞:指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回.epoll工作在非阻塞模式时,才会发挥作用. 我们了解了socket之后已经知道,普通套接字实现的服务端的缺陷:一次只能服务一个 ...

  4. IO多路复用概念性

    sellect.poll.epoll三者的区别 先来了解一下什么是进程切换 为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复以前挂起的某个进程的执行,这种行为为进程的切换,任务切换 ...

  5. IO多路复用的几种实现机制的分析

    http://blog.csdn.net/zhang_shuai_2011/article/details/7675797 select,poll,epoll都是IO多路复用的机制.所谓I/O多路复用 ...

  6. 协程 IO多路复用

    -----------------------------------------------------------------试试并非受罪,问问并不吃亏.善于发问的人,知识丰富. # # ---- ...

  7. 7.24 IO多路复用和协程代码笔记

    1. 复习 # !/usr/bin/env python # !--*--coding:utf-8 --*-- # !@Time :2018/7/23 11:49 # !@Author TrueNew ...

  8. 协程IO多路复用

    协程:单线程下实现并发并发:伪并行,遇到IO就切换,单核下多个任务之间切换执行,给你的效果就是貌似你的几个程序在同时执行.提高效率任务切换 + 保存状态并行:多核cpu,真正的同时执行串行:一个任务执 ...

  9. Java网络编程和NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型

    Java网络编程与NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型 知识点 nio 下 I/O 阻塞与非阻塞实现 SocketChannel 介绍 I/O 多路复用的原理 事件选择器与 ...

随机推荐

  1. Oarcle 之连接查询

            连接查询:连接查询是关系数据库中最主要的查询,主要包括内连接.外连接和交叉连接等.通过连接运算符可以实现多个表查询.连接是关系数据库模型的主要特点,也是它区别于其它类型数据库管理系统的 ...

  2. Spring的事务

    事务:事务指的是逻辑上的一组操作,这组操作要么都成功,要么都失败. Transaction事务的四大特性ACID: 1.Atomicity原子性 事务的操作要么都成功,要么都不做,只要有一个失败,就会 ...

  3. Python hasattr() 函数

    hasattr() 函数用于判断对象是否包含对应的属性.(has attribute) hasattr(object, name) 参数 object -- 对象. name -- 字符串,属性名. ...

  4. layui table数据表格reload where参数保留问题

    layui table数据表格reload where参数保留问题 在使用layui过程中多多少少会遇到些问题 table reload 有个坑:reload时where参数会保留上次的参数,如果用 ...

  5. 创建局域网yum服务器

    首先需要安装createrepo这个软件包,使用yum安装即可 创建软件库文件夹,比如mkdir -p /var/www/html/myrepo/x86_64, 将软件包拷贝到文件夹下. cd到该文件 ...

  6. 常用VIM插件配置

    airline 状态栏美化 除了airline本体还要下airline主题 和打过powerline补丁的字体 常用设置: set laststatus=2 " 总是显示状态栏 set no ...

  7. Bugku-CTF之never give up

    Day23 never give up http://123.206.87.240:8006/test/hello.php   本题要点:url编码,base64编码,代码审计,php函数       ...

  8. loj 6008 餐巾计划 - 费用流

    题目传送门 传送门 题目大意 (经典题还不知道题意?) 容易想到需要把未使用的餐巾和已经使用的餐巾分开. 设$X_i$表示第$i$天已经的使用餐巾的点,设$Y_i$表示第$i$天还未使用的餐巾的点 我 ...

  9. 富文本编辑器Quill(一)简单介绍

    Quill是一个很流行的富文本编辑器,github上star大约21k: github:https://github.com/quilljs/quill/ 官网: https://quilljs.co ...

  10. day13函数的嵌套定义,global、nonlocal关键字,闭包及闭包的运用场景,装饰器

    复习 ''' 1.函数对象:函数名 => 存放的是函数的内存地址 1)函数名 - 找到的是函数的内存地址 2)函数名() - 调用函数 => 函数的返回值 eg:fn()() => ...