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. Android开发实现高德地图定位

    1.获取Key 参考官方文档:http://lbs.amap.com/api/android-location-sdk/guide/create-project/get-key 对于签名文件的获取建议 ...

  2. Web服务器安全设置

    Web服务器安全方面一直重视程度不够,是各种网站经常被黑的主要原因.下面笔者总结了一下关于怎样保证Web服务器安全的措施,希望能给那些服务器尚存在漏洞的用户提供一些帮助. 本文主要以Windows s ...

  3. vSphere Client用户名密码保存记录

    vSphere Client在访问ESXi主机或vCenter后是默认不保存登录用户名和密码的,不过可以通过修改配置文件来保存,方便访问连接. 方法如下: 打开配置文件路径(实际安装路径):D:\Pr ...

  4. ssh 非root用户互信

    之所以要把这个记录下来 是因为它的确和root用户不一样root用户 不需要改动什么权限问题  只要生成私钥/公钥对 即可 但是一样的操作在普通用户上就出了问题了 折腾了老半天 ssh-keygen ...

  5. Python3简明教程(十)—— 异常

    在本节我们学习 Python 的异常以及如何在你的代码中处理它们. 异常 在程序执行过程中发生的任何错误都是异常.每个异常显示一些相关的错误信息,比如你在 Python3 中使用 Python2 独有 ...

  6. VBA Promming——分支语句(解二元一次方程)

    分支语句 If expression1 Then expressions ElseIf expression2 Then expressions Else expression End If 注:VB ...

  7. _ 下划线 vue mixins 混入 变量前有下划线 变量不起作用

    _ 下划线 vue mixins 混入 变量前有下划线 变量不起作用

  8. mkdir touch vim

    vim和touch都用于新建文件 mkdir用于新建文件夹

  9. python⽤户登陆

    ⽤户登陆(三次输错机会)且每次输错误时显示剩余错误次数(提示:使⽤字符串格式化) count = 3 while count < 4: count -= 1 username = input(' ...

  10. 时钟Demo

    其实是一个很简单的Demo,可以编译了拿NSIS打包.最近在做富文本编辑器和补C++不记得的东西吧,项目遥遥无期. //clock.pro #----------------------------- ...