需求:

  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. 示例, linq分组

    public class HIS_CLIREGISTER : BaseModel{ private String _FBCODE;[StringLength(8)]/// <summary> ...

  2. 什么是DevOps

    历史回顾 为了能够更好的理解什么是DevOps,我们很有必要对当时还只有程序员(此前还没有派生出开发者,前台工程师,后台工程师之类)这个称号存在的历史进行一下回顾. 如编程之道中所言: 老一辈的程序员 ...

  3. 认识volatile的工作原理

    前言 在多线程并发编程中synchronized和Volatile都扮演着重要的角色,Volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”.可见性的意思是当 ...

  4. 使用GoldenGate EVENTACTIONS执行数据的实时触发和定制化

    Oracle GoldenGate不仅可以在线实时同步数据(包括增量和存量),也内置有一套事件触发流程,允许用户根据某张表某条记录的某个特殊字段值,触发相应的自定义执行流程,比如接收到某个银行账号的大 ...

  5. jmeter java requst请求

    https://wangym.iteye.com/blog/731729 http://www.cnblogs.com/yangxia-test/p/4019541.html https://blog ...

  6. C语言实例:结构体

    结构体: #include <stdio.h> #include <stdlib.h> //#pragma pack(1) typedef struct{ short i; / ...

  7. 原生JS封装创建多级菜单函数

    手写一个使用原生JS封装的多级菜单的函数,满足以下几点需求. 子类层级不确定,可根据数据自动生成多级菜单. 操作便捷,只需传入一个HTML标签. 缺点: 需要满足特定的数据结构 废话不多说,展示代码. ...

  8. mysql配置外部允许外部连接

    1. 登录到mysql mysql -u root -p 2.进入到mysql 库中 use mysql 3.执行语句 update user set host=‘%’ where user=‘roo ...

  9. 我应该如何在Pycharm中去运行别人的Django项目

    django数据库迁移,本地运行 前言: 从网络上下载好django项目后,在本地用pycharm导入后,并不能运行.此时我们需要添加库和创建数据库. 零:这里是一个基于django写的小项目,可以作 ...

  10. Elasticsearch NEST 控制字段名称命名格式

    在使用NEST操作elasticsearch时,字段名会根据model中字段,默认为首字母小写. 如果需要调整NEST的默认明个规则,可以在 ConnectionSettings中进行自定义. var ...