python I/O多路复用包括3个模块,上一篇已经说过概念,这里我使用的是select模块实现一个ftp并发

服务器端核心代码:

 import socket,select
import queue,os
from conf import setting class select_server(object):
''' ftp服务器核心程序''' def __init__(self,ip,port):
self.server = socket.socket()
self.server.bind((ip,port))
self.inputs = [self.server,] # 收
self.outputs = [] # 发
self.msg = {} # 保存客户端的socket信息及对应的登录账号,密码,登录状态和客户端消息队列
self.client_sock_cmd = {} # 以客户端socket为健名,值为对应客户端的所有信息 def listen(self):
''' 启动监听并设定服务器为非阻塞模式'''
self.server.listen(100)
self.server.setblocking(False) def login_auth(self,name,pwd,auth_file):
''' 进行账户验证'''
if name in auth_file:
if pwd == auth_file[name]['passwd']:
return ''
return ''
return '' def sele(self):
''' 非阻塞模式操作'''
while True:
# 监听客户端的连接,返回3个列表
readable, writeable, exceptionable = select.select(self.inputs, self.outputs, self.inputs)
# 客户端的连接
for s in readable:
if s is self.server: # 如果客户端为新连入的
conn,addr = self.server.accept()
print('已新建立一个客户端的连接',conn)
self.inputs.append(conn) # 添加客户端的socket到inputs列表
conn.setblocking(False)
self.msg[conn] = {} # 初始化客户端的值为一个队列
self.msg[conn]['queue'] = queue.Queue()
self.msg[conn]['account'] = []
self.msg[conn]['status'] = False
conn.send('请输入登录账号:'.encode('utf-8'))
conn.send('请输入登录密码:'.encode('utf-8'))
else:
try:
data = s.recv(1024)
except ConnectionResetError as e: # 客户端中断捕捉的异常
exceptionable.append(s)
break
if data == b'exit':
exceptionable.append(s)
break
self.msg[s]['queue'].put(data) # 将客户端发来的数据保存到队列中
self.outputs.append(s) # 将客户端的socket添加到outputs列表 for w in writeable: # 客户端写列表
data = self.msg[w]['queue'].get()
# print(data)
# 判断账号是否登录,未登录执行如下
if self.msg[w]['status'] == False:
self.msg[w]['account'].append(data.decode())
if len(self.msg[w]['account']) == 2:
# 进行账号密码验证操作
out = self.login_auth(self.msg[w]['account'][0],self.msg[w]['account'][1],setting.account)
if out == '':
self.msg[w]['status'] = True
w.send('login'.encode('utf-8'))
else:
w.send('err'.encode('utf-8'))
exceptionable.append(w)
else:
# 已登录执行如下
# 根据客户端socket进行get和put操作
if w in self.client_sock_cmd:
if self.client_sock_cmd[w]['cmd'] == b'put':
# 进行put上传操作
self.client_sock_cmd[w]['file_io'].write(data)
self.client_sock_cmd[w]['file_io'].flush()
if os.path.getsize(self.client_sock_cmd[w]['filename']) == self.client_sock_cmd[w]['filesize']:
self.client_sock_cmd[w]['file_io'].close()
del self.client_sock_cmd[w]
print('文件保存完毕')
else:
# 进行get下载操作
if data.decode() == self.client_sock_cmd[w]['status']:
# 进行数据get操作在Linux必须要用send指定字节大小,否则报错,在windows下可以一次性发送sendall
# 由于这里使用了select多路复用模式,所以需要每次收发,都需要和客户端进行一次交互动作
if self.client_sock_cmd[w]['filesize'] - self.client_sock_cmd[w]['initsize'] >= 1024:
size = 1024
else:
size = self.client_sock_cmd[w]['filesize'] - self.client_sock_cmd[w]['initsize']
data = self.client_sock_cmd[w]['file_io'].read(size)
self.client_sock_cmd[w]['initsize'] += len(data)
w.send(data)
if self.client_sock_cmd[w]['filesize'] == self.client_sock_cmd[w]['initsize']:
self.client_sock_cmd[w]['file_io'].close()
del self.client_sock_cmd[w]
print('文件发送完毕')
else:
# 根据客户端的socket进行字典初始化
self.client_sock_cmd[w] = {}
self.client_sock_cmd[w]['cmd'] = data.split()[0]
if self.client_sock_cmd[w]['cmd'] == b'put':
# 进行put的初始化操作
self.client_sock_cmd[w]['filename'], self.client_sock_cmd[w]['filesize'] = data.split()[1], int(data.split()[2])
self.client_sock_cmd[w]['filename'] = os.path.join(setting.save_dir,self.client_sock_cmd[w]['filename'])
f = open(self.client_sock_cmd[w]['filename'], 'wb')
self.client_sock_cmd[w]['file_io'] = f
w.send(''.encode('utf-8'))
else:
# 进行get的初始化操作
self.client_sock_cmd[w]['filename'] = data.split()[1]
self.client_sock_cmd[w]['filename'] = os.path.join(setting.save_dir,self.client_sock_cmd[w]['filename'])
if os.path.isfile(self.client_sock_cmd[w]['filename']):
self.client_sock_cmd[w]['status'] = ''
self.client_sock_cmd[w]['initsize'] = 0
self.client_sock_cmd[w]['filesize'] = os.path.getsize(self.client_sock_cmd[w]['filename'])
status_msg = '%s %s' % (self.client_sock_cmd[w]['status'], str(self.client_sock_cmd[w]['filesize']))
f = open(self.client_sock_cmd[w]['filename'], 'rb')
self.client_sock_cmd[w]['file_io'] = f
else:
status_msg = '%s 0' % ('')
exceptionable.append(w)
w.send(status_msg.encode('utf-8'))
self.outputs.remove(w)
# 客户端异常或者退出清空对应的客户端socket信息并关闭连接
for e in exceptionable:
print('客户端退出:',e)
if e in self.msg:
del self.msg[e]
if e in self.outputs:
self.outputs.remove(e)
self.inputs.remove(e)
e.close()

客户端核心代码

 #!/bin/env python
#Author: zhaoyong import socket,os class client_sock(object):
'''
ftp客户端主程序
''' def __init__(self):
''' socket 实例化'''
self.client = socket.socket() def conn(self,ip,port):
''' 连接服务器'''
self.client.connect((ip,port),) def help(self):
''' 帮助信息'''
print('''
上传下载命令如下:
put filename 上传文件
get filename 下载文件
''') def interactive(self):
''' 交互模式'''
while True:
msg = self.client.recv(22).decode()
if msg == 'login':
print('登录成功')
break
if msg == 'err':
exit('登录失败')
info = input(msg).strip()
if not info:
exit('输入为空,退出')
self.client.send(info.encode('utf-8'))
while True:
cmd = input('请输入上传下载命令<exit 退出系统>:').strip()
if not cmd:
print('输入为空,请重新输入')
continue
if cmd == 'exit':
print('客户端退出完成')
self.client.send(b'exit')
break
if len(cmd.split()) == 2:
# 反射调用类方法
if hasattr(self,cmd.split()[0]):
fun = getattr(self,cmd.split()[0])
fun(cmd)
else:
self.help()
else:
self.help() def put(self,cmd):
''' 客户端执行的put功能'''
# 提取文件名
file_name = cmd.split()[1].strip()
if os.path.isfile(file_name):
# 提取上传的文件大小
file_size = os.path.getsize(file_name)
file_mess = '%s %s' % (cmd, str(file_size))
# 将文件命令,文件名,文件大小发送到服务器
self.client.send(file_mess.encode('utf-8'))
# 打开文件读取并发送
mess = self.client.recv(1024).decode()
if mess == '':
f = open(file_name, 'rb')
self.client.sendall(f.read())
f.close()
print('文件上传完毕')
else:
print('文件不存在') def get(self,cmd):
''' 客户端执行的get功能'''
self.client.send(cmd.encode('utf-8'))
# 以下为接收到服务器发来的文件名和文件大小
file_name = cmd.split()[1]
file_mess = self.client.recv(1024).decode()
file_mess = file_mess.split()
file_status, file_size = file_mess[0], int(file_mess[1])
if file_status == '':
print('文件不存在')
return 0
# 接收到服务器端的状态码后,返回给服务器端确认
self.client.send(file_status.encode('utf-8'))
# 打开写文件
init_size = 0
f = open(file_name, 'wb')
while init_size < file_size:
if file_size - init_size >= 1024:
size = 1024
else:
size = file_size - init_size
data = self.client.recv(size)
init_size += len(data)
f.write(data)
f.flush()
# 如果文件没有下载完成,就重复通知服务器
if init_size < file_size:
self.client.send(file_status.encode('utf-8'))
if os.path.getsize(file_name) == file_size:
f.close()
print('文件下载完毕')

关于selectors 模块

举例:

 import socket
import selectors sel = selectors.DefaultSelector() def accept(sock,mask):
conn,addr = sock.accept()
print('fount client',conn)
conn.setblocking(False)
sel.register(conn,selectors.EVENT_READ,read) def read(conn,mask):
data = conn.recv(1024)
print(data)
if data:
conn.send(data)
else:
sel.unregister(conn)
conn.close() server = socket.socket()
server.bind(('0.0.0.0',9999))
server.listen(100)
server.setblocking(False)
sel.register(server,selectors.EVENT_READ,accept)
while True:
events = sel.select()
for key,mask in events:
callback = key.data
callback(key.fileobj, mask)

python学习之-- IO多路复用 select模块的更多相关文章

  1. 第五十五节,IO多路复用select模块加socket模块,伪多线并发

    IO多路复用select模块加socket模块,伪多线并发,并不是真正的多线程并发,实际通过循环等待还是一个一个处理的 IO多路复用,lo就是文件或数据的输入输出,IO多路复用就是可以多用户操作 IO ...

  2. Python实战之IO多路复用select的详细简单练习

    IO多路复用 I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. select   它通过一个select()系统调用来 ...

  3. day36 python学习gevent io 多路复用 socketserver *****

    ---恢复内容开始--- gevent 1.切换+保存状态 2.检测单线程下任务的IO,实现遇到IO自动切换 Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在geven ...

  4. Python IO多路复用select模块

    多路复用的分析实例:服务端.客户端 #服务端配置 from socket import * import time import select server = socket(AF_INET, SOC ...

  5. Python实战之IO多路复用select实例

    Select方法: 句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间)   参数: 可接受四个参数(前三个必须) 返回值 ...

  6. python网络编程——IO多路复用select/poll/epoll的使用

    转载博客: http://www.haiyun.me/archives/1056.html http://www.cnblogs.com/coser/archive/2012/01/06/231521 ...

  7. python网络编程——IO多路复用之select

    1 IO多路复用的概念 原生socket客户端在与服务端建立连接时,即服务端调用accept方法时是阻塞的,同时服务端和客户端在收发数据(调用recv.send.sendall)时也是阻塞的.原生so ...

  8. socket_server源码剖析、python作用域、IO多路复用

    本节内容: 课前准备知识: 函数嵌套函数的使用方法: 我们在使用函数嵌套函数的时候,是学习装饰器的时候,出现过,由一个函数返回值是一个函数体情况. 我们在使用函数嵌套函数的时候,最好也这么写. def ...

  9. python 网络编程 IO多路复用之epoll

    python网络编程——IO多路复用之epoll 1.内核EPOLL模型讲解     此部分参考http://blog.csdn.net/mango_song/article/details/4264 ...

随机推荐

  1. 51nod 1067 Bash游戏 V2

    基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 有一堆石子共有N个.A B两个人轮流拿,A先拿.每次只能拿1,3,4颗,拿到最后1颗石子的人获胜.假设A B都非常聪 ...

  2. Architecture:架构 元素与关系

    http://www.iso-architecture.org/42010/cm/ Systems have architectures. In the Standard, the architect ...

  3. Linux一些常用小命令

    使用xshell连接虚拟机 rz 上传的linux服务器 sz 从服务器上下载 df 查看磁盘大小 -h du 查看所有磁盘(硬盘)大小(-h 可读  -s统计当前目录的大小)du -sh free ...

  4. Asp.Net Core 入门(二)——Startup.cs做了什么

    上篇介绍了Program.cs中Main做了什么,这篇我们来讨论下Startup.cs它又做了什么呢? 我们新建一个Asp.Net Core Mvc项目,先来开一下Startup的代码 public ...

  5. vs 2012安装ASP.NET MVC5

    VS2012能使用MVC5开发,但VS2012不自带MVC5,需要安装“用于 Visual Studio 2012 的 ASP.NET 和 Web 工具 2013.1” 从下面提供的链接下载安装: h ...

  6. 分享点干货(此this非彼this)this的详细解读

    在javascript编程中,this关键字经常让初学者感到迷惑,这里,针对此this根据查阅的资料和个人的理解分享一下,纯手打,大神勿喷. 首先先说一下this的指向,大致可以分为以下四种. 1.作 ...

  7. 秋招复习-C++(三)

    • 数据库 1.数据库的索引有哪些? (1)B树索引:利用B树作为底层数据结构的索引,在B树索引中保存索引列的值和数据表的对应行的ID,每一个叶子结点都存放着一个索引列的值和数据表对应行的ID,通过这 ...

  8. Openjudge-4115-佐助和鸣人

    这一题是一道广搜的题目,首先我们通过读入字符串读入每一行,然后顺带找到鸣人的位置. 然后我们初始化之后,就进行广搜,还是广搜的格式,但是要压入队列的条件我们可以稍微变一变,我们可以直接判断下一个要走的 ...

  9. Django之web本质

    Django之web本质 Web的本质,是基于socket玩的. 在我们上网的过程中,一个访问请求是如何工作的. Web的框架: 网络的连接都是基于Socket 在连接中有TCP/UDP 和HTTP协 ...

  10. mysql启动问题

    /usr/local/mysql/bin/mysqld: Can't find file: './mysql/plugin.frm' (errno: 13 - Permission denied) - ...