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的内建模块collections的教程
Apr 28 Python
简单了解什么是神经网络
Dec 23 Python
Ubuntu下使用Python实现游戏制作中的切分图片功能
Mar 30 Python
Python3中的json模块使用详解
May 05 Python
Python实现爬虫抓取与读写、追加到excel文件操作示例
Jun 27 Python
解决使用pycharm提交代码时冲突之后文件丢失找回的方法
Aug 05 Python
numpy中的ndarray方法和属性详解
May 27 Python
Python Opencv 通过轨迹(跟踪)栏实现更改整张图像的背景颜色
Mar 09 Python
使用python脚本自动生成K8S-YAML的方法示例
Jul 12 Python
django表单中的按钮获取数据的实例分析
Jul 31 Python
Python使用Selenium模拟浏览器自动操作功能
Sep 08 Python
Matlab求解数组中的最大值及它所在的具体位置
Apr 16 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来改写404错误页让你的页面更友好
2013/01/24 PHP
PHP遍历数组的方法汇总
2015/04/30 PHP
Javascript 继承机制实例
2009/08/12 Javascript
jQuery 操作下拉列表框实现代码
2010/02/22 Javascript
修改jquery里的dialog对话框插件为框架页(iframe) 的方法
2010/09/14 Javascript
jquery全选/全不选/反选另一种实现方法(配合原生js)
2013/04/07 Javascript
动态加载jquery库的方法
2014/02/12 Javascript
javascript日期格式化方法小结
2015/12/17 Javascript
jquery的ajax提交form表单的两种方法小结(推荐)
2016/05/25 Javascript
jqGrid用法汇总(全经典)
2016/06/28 Javascript
基于JavaScript实现滑动门效果
2017/03/16 Javascript
jQuery插件FusionCharts实现的3D帕累托图效果示例【附demo源码】
2017/03/25 jQuery
jQuery Validate插件ajax方式验证输入值的实例
2017/12/21 jQuery
详解用Node.js写一个简单的命令行工具
2018/03/01 Javascript
理顺8个版本vue的区别(小结)
2018/09/17 Javascript
vue实现二级导航栏效果
2019/10/19 Javascript
[02:34]DOTA2英雄基础教程 幽鬼
2014/01/02 DOTA
[03:09]显微镜下的DOTA2第一期——带你走进华丽的DOTA2世界
2014/06/20 DOTA
快速了解python leveldb
2018/01/18 Python
TensorFlow平台下Python实现神经网络
2018/03/10 Python
python自动化测试之如何解析excel文件
2019/06/27 Python
python安装dlib库报错问题及解决方法
2020/03/16 Python
详解Python 实现 ZeroMQ 的三种基本工作模式
2020/03/24 Python
不到20行实现Python代码即可制作精美证件照
2020/04/24 Python
Python面向对象多态实现原理及代码实例
2020/09/16 Python
HTMl5的存储方式sessionStorage和localStorage详解
2014/03/18 HTML / CSS
Quiksilver荷兰官方网站:冲浪和滑雪板
2019/11/16 全球购物
请问如下代码执行后a和b的值分别是什么
2016/05/05 面试题
工地安全检查制度
2014/02/04 职场文书
前处理班长职位说明书
2014/03/01 职场文书
中学生运动会新闻稿
2014/09/24 职场文书
2014年幼儿园德育工作总结
2014/12/17 职场文书
商务宴请邀请函范文
2015/02/02 职场文书
2015年度酒店客房部工作总结
2015/05/25 职场文书
用golang如何替换某个文件中的字符串
2021/04/25 Golang
Mysql数据库手动及定时备份步骤
2021/11/07 MySQL