zoukankan      html  css  js  c++  java
  • python学习之-- IO多路复用 select模块

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

    服务器端核心代码:

      1 import socket,select
      2 import queue,os
      3 from conf import setting
      4 
      5 
      6 class select_server(object):
      7     ''' ftp服务器核心程序'''
      8 
      9     def __init__(self,ip,port):
     10         self.server = socket.socket()
     11         self.server.bind((ip,port))
     12         self.inputs = [self.server,]  #
     13         self.outputs = []  #
     14         self.msg = {}  # 保存客户端的socket信息及对应的登录账号,密码,登录状态和客户端消息队列
     15         self.client_sock_cmd = {}  # 以客户端socket为健名,值为对应客户端的所有信息
     16 
     17     def listen(self):
     18         ''' 启动监听并设定服务器为非阻塞模式'''
     19         self.server.listen(100)
     20         self.server.setblocking(False)
     21 
     22     def login_auth(self,name,pwd,auth_file):
     23         ''' 进行账户验证'''
     24         if name in auth_file:
     25             if pwd == auth_file[name]['passwd']:
     26                 return '200'
     27             return '400'
     28         return '400'
     29 
     30     def sele(self):
     31         ''' 非阻塞模式操作'''
     32         while True:
     33             # 监听客户端的连接,返回3个列表
     34             readable, writeable, exceptionable = select.select(self.inputs, self.outputs, self.inputs)
     35             # 客户端的连接
     36             for s in readable:
     37                 if s is self.server:  # 如果客户端为新连入的
     38                     conn,addr = self.server.accept()
     39                     print('已新建立一个客户端的连接',conn)
     40                     self.inputs.append(conn)  # 添加客户端的socket到inputs列表
     41                     conn.setblocking(False)
     42                     self.msg[conn] = {}  # 初始化客户端的值为一个队列
     43                     self.msg[conn]['queue'] = queue.Queue()
     44                     self.msg[conn]['account'] = []
     45                     self.msg[conn]['status'] = False
     46                     conn.send('请输入登录账号:'.encode('utf-8'))
     47                     conn.send('请输入登录密码:'.encode('utf-8'))
     48                 else:
     49                     try:
     50                         data = s.recv(1024)
     51                     except ConnectionResetError as e:  # 客户端中断捕捉的异常
     52                         exceptionable.append(s)
     53                         break
     54                     if data == b'exit':
     55                         exceptionable.append(s)
     56                         break
     57                     self.msg[s]['queue'].put(data)  # 将客户端发来的数据保存到队列中
     58                     self.outputs.append(s)  # 将客户端的socket添加到outputs列表
     59 
     60             for w in writeable:  # 客户端写列表
     61                 data = self.msg[w]['queue'].get()
     62                 # print(data)
     63                 # 判断账号是否登录,未登录执行如下
     64                 if self.msg[w]['status'] == False:
     65                     self.msg[w]['account'].append(data.decode())
     66                     if len(self.msg[w]['account']) == 2:
     67                         # 进行账号密码验证操作
     68                         out = self.login_auth(self.msg[w]['account'][0],self.msg[w]['account'][1],setting.account)
     69                         if out == '200':
     70                             self.msg[w]['status'] = True
     71                             w.send('login'.encode('utf-8'))
     72                         else:
     73                             w.send('err'.encode('utf-8'))
     74                             exceptionable.append(w)
     75                 else:
     76                 # 已登录执行如下
     77                 # 根据客户端socket进行get和put操作
     78                     if w in self.client_sock_cmd:
     79                         if self.client_sock_cmd[w]['cmd'] == b'put':
     80                             # 进行put上传操作
     81                             self.client_sock_cmd[w]['file_io'].write(data)
     82                             self.client_sock_cmd[w]['file_io'].flush()
     83                             if os.path.getsize(self.client_sock_cmd[w]['filename']) == self.client_sock_cmd[w]['filesize']:
     84                                 self.client_sock_cmd[w]['file_io'].close()
     85                                 del self.client_sock_cmd[w]
     86                                 print('文件保存完毕')
     87                         else:
     88                             # 进行get下载操作
     89                             if data.decode() == self.client_sock_cmd[w]['status']:
     90                                 # 进行数据get操作在Linux必须要用send指定字节大小,否则报错,在windows下可以一次性发送sendall
     91                                 # 由于这里使用了select多路复用模式,所以需要每次收发,都需要和客户端进行一次交互动作
     92                                 if self.client_sock_cmd[w]['filesize'] - self.client_sock_cmd[w]['initsize'] >= 1024:
     93                                     size = 1024
     94                                 else:
     95                                     size = self.client_sock_cmd[w]['filesize'] - self.client_sock_cmd[w]['initsize']
     96                                 data = self.client_sock_cmd[w]['file_io'].read(size)
     97                                 self.client_sock_cmd[w]['initsize'] += len(data)
     98                                 w.send(data)
     99                                 if self.client_sock_cmd[w]['filesize'] == self.client_sock_cmd[w]['initsize']:
    100                                     self.client_sock_cmd[w]['file_io'].close()
    101                                     del self.client_sock_cmd[w]
    102                                     print('文件发送完毕')
    103                     else:
    104                         # 根据客户端的socket进行字典初始化
    105                         self.client_sock_cmd[w] = {}
    106                         self.client_sock_cmd[w]['cmd'] = data.split()[0]
    107                         if self.client_sock_cmd[w]['cmd'] == b'put':
    108                             # 进行put的初始化操作
    109                             self.client_sock_cmd[w]['filename'], self.client_sock_cmd[w]['filesize'] = data.split()[1], int(data.split()[2])
    110                             self.client_sock_cmd[w]['filename'] = os.path.join(setting.save_dir,self.client_sock_cmd[w]['filename'])
    111                             f = open(self.client_sock_cmd[w]['filename'], 'wb')
    112                             self.client_sock_cmd[w]['file_io'] = f
    113                             w.send('200'.encode('utf-8'))
    114                         else:
    115                             # 进行get的初始化操作
    116                             self.client_sock_cmd[w]['filename'] = data.split()[1]
    117                             self.client_sock_cmd[w]['filename'] = os.path.join(setting.save_dir,self.client_sock_cmd[w]['filename'])
    118                             if os.path.isfile(self.client_sock_cmd[w]['filename']):
    119                                 self.client_sock_cmd[w]['status'] = '200'
    120                                 self.client_sock_cmd[w]['initsize'] = 0
    121                                 self.client_sock_cmd[w]['filesize'] = os.path.getsize(self.client_sock_cmd[w]['filename'])
    122                                 status_msg = '%s %s' % (self.client_sock_cmd[w]['status'], str(self.client_sock_cmd[w]['filesize']))
    123                                 f = open(self.client_sock_cmd[w]['filename'], 'rb')
    124                                 self.client_sock_cmd[w]['file_io'] = f
    125                             else:
    126                                 status_msg = '%s 0' % ('404')
    127                                 exceptionable.append(w)
    128                             w.send(status_msg.encode('utf-8'))
    129                 self.outputs.remove(w)
    130             # 客户端异常或者退出清空对应的客户端socket信息并关闭连接
    131             for e in exceptionable:
    132                 print('客户端退出:',e)
    133                 if e in self.msg:
    134                     del self.msg[e]
    135                 if e in self.outputs:
    136                     self.outputs.remove(e)
    137                 self.inputs.remove(e)
    138                 e.close()
    View Code

    客户端核心代码

      1 #!/bin/env python
      2 #Author: zhaoyong
      3 
      4 import socket,os
      5 
      6 
      7 class client_sock(object):
      8     '''
      9         ftp客户端主程序
     10     '''
     11 
     12     def __init__(self):
     13         ''' socket 实例化'''
     14         self.client = socket.socket()
     15 
     16     def conn(self,ip,port):
     17         ''' 连接服务器'''
     18         self.client.connect((ip,port),)
     19 
     20     def help(self):
     21         ''' 帮助信息'''
     22         print('''
     23             上传下载命令如下:
     24             put filename  上传文件
     25             get filename  下载文件
     26         ''')
     27 
     28     def interactive(self):
     29         ''' 交互模式'''
     30         while True:
     31             msg = self.client.recv(22).decode()
     32             if msg == 'login':
     33                 print('登录成功')
     34                 break
     35             if msg == 'err':
     36                 exit('登录失败')
     37             info = input(msg).strip()
     38             if not info:
     39                 exit('输入为空,退出')
     40             self.client.send(info.encode('utf-8'))
     41         while True:
     42             cmd = input('请输入上传下载命令<exit 退出系统>:').strip()
     43             if not cmd:
     44                 print('输入为空,请重新输入')
     45                 continue
     46             if cmd == 'exit':
     47                 print('客户端退出完成')
     48                 self.client.send(b'exit')
     49                 break
     50             if len(cmd.split()) == 2:
     51                 # 反射调用类方法
     52                 if hasattr(self,cmd.split()[0]):
     53                     fun = getattr(self,cmd.split()[0])
     54                     fun(cmd)
     55                 else:
     56                     self.help()
     57             else:
     58                 self.help()
     59 
     60     def put(self,cmd):
     61         ''' 客户端执行的put功能'''
     62         # 提取文件名
     63         file_name = cmd.split()[1].strip()
     64         if os.path.isfile(file_name):
     65             # 提取上传的文件大小
     66             file_size = os.path.getsize(file_name)
     67             file_mess = '%s %s' % (cmd, str(file_size))
     68             # 将文件命令,文件名,文件大小发送到服务器
     69             self.client.send(file_mess.encode('utf-8'))
     70             # 打开文件读取并发送
     71             mess = self.client.recv(1024).decode()
     72             if mess == '200':
     73                 f = open(file_name, 'rb')
     74                 self.client.sendall(f.read())
     75                 f.close()
     76                 print('文件上传完毕')
     77         else:
     78             print('文件不存在')
     79 
     80     def get(self,cmd):
     81         ''' 客户端执行的get功能'''
     82         self.client.send(cmd.encode('utf-8'))
     83         # 以下为接收到服务器发来的文件名和文件大小
     84         file_name = cmd.split()[1]
     85         file_mess = self.client.recv(1024).decode()
     86         file_mess = file_mess.split()
     87         file_status, file_size = file_mess[0], int(file_mess[1])
     88         if file_status == '404':
     89             print('文件不存在')
     90             return 0
     91         # 接收到服务器端的状态码后,返回给服务器端确认
     92         self.client.send(file_status.encode('utf-8'))
     93         # 打开写文件
     94         init_size = 0
     95         f = open(file_name, 'wb')
     96         while init_size < file_size:
     97             if file_size - init_size >= 1024:
     98                 size = 1024
     99             else:
    100                 size = file_size - init_size
    101             data = self.client.recv(size)
    102             init_size += len(data)
    103             f.write(data)
    104             f.flush()
    105             # 如果文件没有下载完成,就重复通知服务器
    106             if init_size < file_size:
    107                 self.client.send(file_status.encode('utf-8'))
    108         if os.path.getsize(file_name) == file_size:
    109             f.close()
    110             print('文件下载完毕')
    View Code

    关于selectors 模块

    举例:

     1 import socket
     2 import selectors
     3 
     4 sel = selectors.DefaultSelector()
     5 
     6 def accept(sock,mask):
     7     conn,addr = sock.accept()
     8     print('fount client',conn)
     9     conn.setblocking(False)
    10     sel.register(conn,selectors.EVENT_READ,read)
    11 
    12 def read(conn,mask):
    13     data = conn.recv(1024)
    14     print(data)
    15     if data:
    16         conn.send(data)
    17     else:
    18         sel.unregister(conn)
    19         conn.close()
    20 
    21 server = socket.socket()
    22 server.bind(('0.0.0.0',9999))
    23 server.listen(100)
    24 server.setblocking(False)
    25 sel.register(server,selectors.EVENT_READ,accept)
    26 while True:
    27     events = sel.select()
    28     for key,mask in events:
    29         callback = key.data
    30         callback(key.fileobj, mask)
    View Code
  • 相关阅读:
    word转HTML并使用于浏览器界面
    配置wampserver出现服务器错误问题
    SVN使用及配置pycharm、本地Linux虚拟机教程
    加载本地json文件,调试出现 Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.解决方法
    转载:有关Html中<a>、<link>和<script>标签中相对路径与绝对路径的问题总结
    asp.net 登出设置
    Maven依赖中的scope详解
    什么情况下用resultType和 resultMap
    修改tomcat默认端口号8080
    SpringMVC
  • 原文地址:https://www.cnblogs.com/zy6103/p/7053143.html
Copyright © 2011-2022 走看看