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 相关文章推荐
忘记ftp密码使用python ftplib库暴力破解密码的方法示例
Jan 22 Python
Python爬虫框架Scrapy安装使用步骤
Apr 01 Python
python网络编程学习笔记(四):域名系统
Jun 09 Python
Django与遗留的数据库整合的方法指南
Jul 24 Python
浅谈Python爬取网页的编码处理
Nov 04 Python
Python基于identicon库创建类似Github上用的头像功能
Sep 25 Python
python实现用户管理系统
Jan 10 Python
python opencv实现证件照换底功能
Aug 19 Python
Python3.7基于hashlib和Crypto实现加签验签功能(实例代码)
Dec 04 Python
python创建ArcGIS shape文件的实现
Dec 06 Python
Pytorch中的自动求梯度机制和Variable类实例
Feb 29 Python
python基于pygame实现飞机大作战小游戏
Nov 19 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
php5.2.0内存管理改进
2007/01/22 PHP
让Json更懂中文(JSON_UNESCAPED_UNICODE)
2011/10/27 PHP
如何使用php判断所处服务器操作系统的类型
2013/06/20 PHP
php中替换字符串中的空格为逗号','的方法
2014/06/09 PHP
解析PHP之提取多维数组指定列的方法
2017/01/03 PHP
php生成毫秒时间戳的实例讲解
2017/09/22 PHP
php扩展开发入门demo示例
2019/09/23 PHP
转义字符(\)对JavaScript中JSON.parse的影响概述
2013/07/17 Javascript
运用JQuery的toggle实现网页加载完成自动弹窗
2014/03/18 Javascript
如何在MVC应用程序中使用Jquery
2014/11/17 Javascript
JavaScript从数组中删除指定值元素的方法
2015/03/18 Javascript
javascript实现点击提交按钮后显示loading的方法
2015/07/03 Javascript
jQuery带时间的日期控件代码分享
2015/08/26 Javascript
基于JQuery和CSS3实现仿Apple TV海报背景视觉差特效源码分享
2015/09/21 Javascript
Nodejs抓取html页面内容(推荐)
2016/08/11 NodeJs
jQuery Easyui datagrid editor为combobox时指定数据源实例
2016/12/19 Javascript
JavaScript中this的全面解析及常见实例
2019/05/14 Javascript
vue在App.vue文件中监听路由变化刷新页面操作
2020/08/14 Javascript
在Django的session中使用User对象的方法
2015/07/23 Python
批量将ppt转换为pdf的Python代码 只要27行!
2018/02/26 Python
pycharm创建一个python包方法图解
2019/04/10 Python
python爬虫开发之selenium模块详细使用方法与实例全解
2020/03/09 Python
python 可视化库PyG2Plot的使用
2021/01/21 Python
通过CSS3的object-fit来调整图片适配尺寸的技巧简介
2016/02/27 HTML / CSS
HTML5 用动画的表现形式装载图像
2016/03/08 HTML / CSS
浅谈HTML5 & CSS3的新交互特性
2016/07/19 HTML / CSS
京东港澳售:京东直邮港澳台
2018/01/31 全球购物
HEMA英国:荷兰原创设计
2018/08/28 全球购物
介绍一下Cookie和Session及他们之间的区别
2012/11/20 面试题
PPP协议组成及简述协议协商的基本过程
2015/05/28 面试题
大学生演讲稿
2014/04/25 职场文书
2014年技术工作总结范文
2014/11/20 职场文书
党风廉正建设个人工作总结
2015/03/06 职场文书
门球健将观后感
2015/06/16 职场文书
2019自荐信该如何写呢?
2019/07/05 职场文书
Spring Boot DevTools 全局配置学习指南
2022/03/31 Java/Android