python学习之-- IO多路复用 select模块
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模块的更多相关文章
- 第五十五节,IO多路复用select模块加socket模块,伪多线并发
IO多路复用select模块加socket模块,伪多线并发,并不是真正的多线程并发,实际通过循环等待还是一个一个处理的 IO多路复用,lo就是文件或数据的输入输出,IO多路复用就是可以多用户操作 IO ...
- Python实战之IO多路复用select的详细简单练习
IO多路复用 I/O多路复用指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作. select 它通过一个select()系统调用来 ...
- day36 python学习gevent io 多路复用 socketserver *****
---恢复内容开始--- gevent 1.切换+保存状态 2.检测单线程下任务的IO,实现遇到IO自动切换 Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在geven ...
- Python IO多路复用select模块
多路复用的分析实例:服务端.客户端 #服务端配置 from socket import * import time import select server = socket(AF_INET, SOC ...
- Python实战之IO多路复用select实例
Select方法: 句柄列表11, 句柄列表22, 句柄列表33 = select.select(句柄序列1, 句柄序列2, 句柄序列3, 超时时间) 参数: 可接受四个参数(前三个必须) 返回值 ...
- python网络编程——IO多路复用select/poll/epoll的使用
转载博客: http://www.haiyun.me/archives/1056.html http://www.cnblogs.com/coser/archive/2012/01/06/231521 ...
- python网络编程——IO多路复用之select
1 IO多路复用的概念 原生socket客户端在与服务端建立连接时,即服务端调用accept方法时是阻塞的,同时服务端和客户端在收发数据(调用recv.send.sendall)时也是阻塞的.原生so ...
- socket_server源码剖析、python作用域、IO多路复用
本节内容: 课前准备知识: 函数嵌套函数的使用方法: 我们在使用函数嵌套函数的时候,是学习装饰器的时候,出现过,由一个函数返回值是一个函数体情况. 我们在使用函数嵌套函数的时候,最好也这么写. def ...
- python 网络编程 IO多路复用之epoll
python网络编程——IO多路复用之epoll 1.内核EPOLL模型讲解 此部分参考http://blog.csdn.net/mango_song/article/details/4264 ...
随机推荐
- java.lang.NoSuchMethodError: org.hibernate.cfg.Environment.verifyProperties
我在使用jpa2+spring4+hibernate4 的时候,报错java.lang.NoSuchMethodError: org.hibernate.cfg.Environment.verifyP ...
- Android开发精品收藏贴
各种下拉刷新效果: https://github.com/scwang90/SmartRefreshLayout
- 架包Error inflating class错误
当引用架包后,出现Error inflating class错误时通常要检测架包是否正确引用: 1.首先将你所需要的架包拷贝到工程目录下: 2.右击工程,选择Build Path-->confi ...
- expand - 把 tab 符转换为空格符
总览 (SYNOPSIS) ../src/expand [OPTION]... [FILE]... 描述 (DESCRIPTION) 把 各文件 FILE 中的 tab 符 转换为 空格符, 然后 写 ...
- vue 高度 动态更新计算 calcHeight watch $route
vue 高度 动态更新计算 calcHeight () { // this.tableHeight = window.innerHeight - 210 } }, mounted () { // co ...
- MySQL系列(三)--数据库结构优化
良好的数据库逻辑设计和物理设计是数据库高性能的基础,所以对于数据库结构优化是很有必要的 数据库结构优化目的: 1.减少数据的冗余 2.尽量避免在数据插入.删除和更新异常 例如:有一张设计不得当的学生选 ...
- Windows 7桌面显示图标窗口句柄的获取
在windows XP时代,我们获取桌面图标窗口的句柄往往用一下语句: HWND hwndParent = ::FindWindow( "Progman", "Progr ...
- 背包DP || Codeforces 544C Writing Code
程序员写bug的故事23333 题意:n个程序员,一共写m行程序,最多产生b个bug,问方案数 思路:f[i][j]表示写了i行,产生了j个bug的方案数,因为每个人都是可以独立的,所以i循环到n都做 ...
- Opencv竟然有中文资料
最近对于OpenCV看的较多,竟然不知不觉找到了一个中文网站,对于母语真的桥开心的嘻嘻 直方图均衡化: http://www.opencv.org.cn/opencvdoc/2.3.2/html/do ...
- C++构造函数(复制构造函数)、析构函数
注:若类中没有显示的写如下函数,编译会自动生成:默认复制构造函数.默认赋值构造函数(浅拷贝).默认=运算符重载函数(浅拷贝).析构函数: 1.默认构造函数(默认值)构造函数的作用:初始化对象的数据成员 ...