Python中使用select模块实现非阻塞的IO


Posted in Python onFebruary 03, 2015

Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作”套接字”,用于描述IP地址和端口,是一个通信链的句柄。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。?百度百科

socket如此重要,现在的网络编程几乎都是用的它,它起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用打开,读写,关闭的模式来操作。然而,对于网络服务来说,往往针对大量的客户群体,例如web,对于这类服务,必须要保证既能并行处理请求,又能保证服务的稳定。但传统的socket在处理并发方面有所欠缺,借助与select模块,能够较好的是要非阻塞的IO。

Python中的select模块以列表形式接受四个参数,分别是需要监控的可读文件对象,可写文件对象,产生异常的文件对象和超时设置,当监控的对象发生变化时,select会返回发生变化的对象列表。下面是用select实现一个简单的聊天室:

#!/usr/bin/env python
#*-* coding:utf-8 *-*
import socket
import select
import sys
import signal
class ChatServer():
  def __init__(self,host,port,timeout=10,backlog=5):
    #记录连接的客户端数量
    self.clients =0
    #存储连接的客户端socket和地址对应的字典
    self.clientmap={}
    #存储连接的客户端socket
    self.outputs = []
    #建立socket
    self.server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    self.server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
    self.server.bind((host,port))
    self.server.listen(backlog)
    #增加信号处理
    signal.signal(signal.SIGINT,self.sighandler) 
  def sighandler(self):
    sys.stdout.write("Shutdown Server......\n")
    #向已经连接客户端发送关系信息,并主动关闭socket
    for output in self.outputs:
      output.send("Shutdown Server")
      output.close()
    #关闭listen
    self.server.close()
    sys.stdout.flush()
  #主函数,用来启动服务器
  def run(self):
    #需要监听的可读对象
    inputs=[self.server]
    
    runing=True
    #添加监听主循环
    while runing:
      try:
        readable,writeable,exceptional = select.select(inputs,self.outputs,[])
        #此处会被select模块阻塞,只有当监听的三个参数发生变化时,select才会返回
      except select.error,e:
        break
      #当返回的readable中含有本地socket的信息时,表示有客户端正在请求连接
      if self.server in readable:
        #接受客户端连接请求
        client,addr=self.server.accept()
        sys.stdout.write("New Connection from %s\n"%str(addr))
        sys.stdout.flush()
        #更新服务器上客户端连接情况
        #1,数量加1
        #2,self.outputs增加一列
        #3,self.clientmap增加一对
        #4, 给input添加可读监控
        self.clients += 1
        self.outputs.append(client)
        self.clientmap[client]=addr
        inputs.append(client)
      
      #readable中含有已经添加的客户端socket,并且可读
      #说明 1,客户端有数据发送过来或者 2,客户端请求关闭
      elif len(readable) != 0:
        #1, 取出这个列表中的socket
        csock=readable[0]
        #2, 根据这个socket,在事先存放的clientmap中,去除客户端的地址,端口的详细信息
        host,port = self.clientmap[csock]
        #3,取数据, 或接受关闭请求,并处理
        #注意,这个操作是阻塞的,但是由于数据是在本地缓存之后,所以速度会非常快
        try:
          data = csock.recv(1024).strip()
          for cs in self.outputs:
            if cs != csock:
              cs.send("%s\n"%data)
        except socket.error,e:
          self.clients -= 1
          inputs.remove(csock)
          self.outputs.remove(csock)
          del self.clientmap[csock]
      #print self.outputs
    self.server.close()
        
if __name__ == "__main__":
  chat=ChatServer("",8008)
  chat.run()

运行这个脚本,然后用任意客户端如telnet或netcat连接8008端口,多个客户端之间就可以进行对话。

其实select模块本身是阻塞的,当需要监控的socket发生变化时,select作出返回,下面的程序会继续执行,程序根据select的返回值,对各种情况作出处理。

Python 相关文章推荐
基于Python中单例模式的几种实现方式及优化详解
Jan 09 Python
python使用Pycharm创建一个Django项目
Mar 05 Python
Python  unittest单元测试框架的使用
Sep 08 Python
详解如何设置Python环境变量?
May 13 Python
python使用pygame模块实现坦克大战游戏
Mar 25 Python
关于PyTorch 自动求导机制详解
Aug 18 Python
详解Anconda环境下载python包的教程(图形界面+命令行+pycharm安装)
Nov 11 Python
将python安装信息加入注册表的示例
Nov 20 Python
Pytorch之finetune使用详解
Jan 18 Python
tensorflow的ckpt及pb模型持久化方式及转化详解
Feb 12 Python
python3将变量写入SQL语句的实现方式
Mar 02 Python
python神经网络 使用Keras构建RNN训练
May 04 Python
Python异常学习笔记
Feb 03 #Python
Python中的迭代器漫谈
Feb 03 #Python
Python描述器descriptor详解
Feb 03 #Python
理解Python中的With语句
Feb 02 #Python
Linux环境下MySQL-python安装过程分享
Feb 02 #Python
Python中用pycurl监控http响应时间脚本分享
Feb 02 #Python
Python列表(list)常用操作方法小结
Feb 02 #Python
You might like
利用PHP实现与ASP Banner组件相似的类
2006/10/09 PHP
使用PHPExcel操作Excel用法实例分析
2015/03/26 PHP
浅谈COOKIE和SESSION区别
2015/07/19 PHP
php mysql操作mysql_connect连接数据库实例详解
2016/12/26 PHP
ThinkPHP5.1+Ajax实现的无刷新分页功能示例
2020/02/10 PHP
document.all还是document.getElementsByName?
2006/07/21 Javascript
Ext 今日学习总结
2010/09/19 Javascript
JS组件Form表单验证神器BootstrapValidator
2016/01/26 Javascript
jQuery+ajax实现实用的点赞插件代码
2016/07/06 Javascript
谈谈对vue响应式数据更新的误解
2017/08/01 Javascript
Mongoose中document与object的区别示例详解
2017/09/18 Javascript
浅谈在vue中用webpack打包之后运行文件的问题以及相关配置方法
2018/02/21 Javascript
vue.js层叠轮播效果的实例代码
2018/11/08 Javascript
详解如何在Vue项目中发送jsonp请求
2019/10/25 Javascript
微信小程序scroll-view锚点链接滚动跳转功能
2019/12/12 Javascript
怎么理解wx.navigateTo的events参数使用详情
2020/05/18 Javascript
Python中列表的一些基本操作知识汇总
2015/05/20 Python
详解Python开发中如何使用Hook技巧
2017/11/01 Python
python实现多线程网页下载器
2018/04/15 Python
python操作excel的方法(xlsxwriter包的使用)
2018/06/11 Python
Python使用matplotlib实现基础绘图功能示例
2018/07/03 Python
python GUI库图形界面开发之PyQt5信号与槽的高级使用技巧装饰器信号与槽详细使用方法与实例
2020/03/06 Python
jupyter使用自动补全和切换默认浏览器的方法
2020/11/18 Python
kmart凯马特官网:美国最大的打折零售商和全球最大的批发商之一
2016/11/17 全球购物
中东奢侈品市场:Coveti
2019/05/12 全球购物
优秀实习自我鉴定
2013/12/04 职场文书
前处理组长岗位职责
2014/03/01 职场文书
篮球赛口号
2014/06/18 职场文书
运输企业安全生产责任书
2014/07/28 职场文书
停发工资证明范本
2015/06/12 职场文书
2015年环境监察工作总结
2015/07/23 职场文书
毕业班工作总结
2015/08/10 职场文书
班主任经验交流心得体会
2015/11/02 职场文书
python批量创建变量并赋值操作
2021/06/03 Python
详解jQuery的核心函数和事件处理
2022/02/18 jQuery
Flink 侧流输出源码示例解析
2022/09/23 Servers