在SocketServer模块的学习中,我们了解了多线程和多进程简单Server的实现,使用多线程、多进程技术的服务端为每一个新的client连接创建一个新的进/线程,当client数量较多时,这种技术也将带来巨大的开销,服务器的内存毕竟是有限的,而客户的量级可能非常庞大,因此为每个客户端连接创建单独的进/线程可能并不实际。

  另一种提升服务器性能的网络编程模式是事件驱动的(异步)编程,这里所说的“事件”通常是指:客户端连接到来、套接字有可读数据、套接字可写等。服务器时刻处在一个时刻等待这些事件的循环中,一旦这些常见的某类事件发生,服务端分别进行一定的响应,然后继续等待下一个事件的发生。

  Python 对事件驱动的网络编程提供的支持包括:

    select 模块 —— 低层次 select, epoll 等异步编程机制;

    asyncore, asynchat 模块 —— 高层次的异步编程机制,在Python 3中合并为 asyncio 模块;

    Twisted 框架 —— Twisted 是一个功能强大的Python事件驱动编程框架。

  本文将会学习 select 模块提供的 select、epoll 机制。

Select 编程

  select 的中文含义是”选择“,select机制也如其名,监听一些 server 关心的套接字、文件等对象,关注他们是否可读、可写、发生异常等事件。一旦出现某个 select 关注的事件,select 会对相应的套接字或文件进行特定的处理,这就是 select 机制最主要的功能。

  select 机制可以只使用一个进程/线程来处理多个 socket 或其他对象,因此又被称为I/O复用。

  关于 select 机制的进程阻塞形式,与普通的套接字略有不同。socket 对象可能阻塞在 accept(), recvfrom()等方法上,以 recvfrom() 方法为例,当执行到 socket.recvfrom() 这一句时,就会调用一个系统调用询问内核:client / server 发来的数据包准备好了没?此时从进程空间切換到内核地址空间,内核可能需要等数据包完全到达,然后将数据复制到程序的地址空间后,recvfrom() 才会返回,接下来进程继续执行,对读取到的数据进行必要的处理。

  而使用 select 函数编程时,同样针对上面的 recvfrom() 方法,进程会阻塞在 select() 调用上,等待出现一个或多个套接字对象满足可读事件,当内核将数据准备好后,select() 返回某个套接字对象可读这一条件,随后再调用 recvfrom() 将数据包从内核复制到进程地址空间。

  所以可见,如果仅仅从单个套接字的处理来看,select() 反倒性能更低,因为 select 机制使用两个系统调用。但 select 机制的优势就在于它可以同时等待多个 fd 就绪,而当某个 fd 发生满足我们关心的事件时,就对它执行特定的操作。

  调用 select 模块提供的 select 函数可以实现 Select 编程:

select(inputs, outputs, excepts, timeout=None)

  参数说明:

  inputs, outputs, excepts 是分别由等待输入事件、输出事件和异常条件的 socket 对象组成的列表;

  timeout——数字或者None,上一篇文章中已经介绍过 Python套接字对象的超时行为,这里select与套接字的超时行为类似:如果 timeout 是 None,则 select 在阻塞调用上会一直等待直到事件发生;如果 timeout 是一个非 0 数字,则 select 最多等待 timeout 秒;如果 timeout 是0,则 select 遇到阻塞的调用时会立即返回,一刻也不等待。

  返回值:

  select() 返回一个 (i, o, e) 形式的三元组,其中i, o, e 分别都是列表,列表 i 中的每个元素都收到了 input 事件,列表 o 中的每个元素都收到了 output 事件,列表 e 中的每个元素则发生了异常。

  

  Select 编程的特点是 select() 参数中的 inputs, outputs, excepts 可以不止 socket 对象,而只要是支持无参数调用 fileno() 方法,返回相应 fd 的对象均可,比如 SocketServer 模块中提供的几种 server 类型就支持这一方法,所以在 select() 参数的某个列表中,就可以包含这些 Server 类型的实例。在 Unix 平台上,select 机制也支持并不对应于 socket 对象的文件描述字。

例:

  使用 select 网络编程模型实现一个简单的回显服务器

#!-*-encoding:utf-8-*-
import socket
import select sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.1', 4424))
sock.listen(10) ins = [sock, ]
ous = []
# socket -> data to send
data = {}
# socket -> client address, which is (host, port) tuple
adrs = {} try:
print("Select Server Start Working!")
while True:
i, o, e = select.select(ins, ous, [])
for x in i: # 处理发生 input 事件的套接字
if x is sock:
# 监听套接字上发生input事件,说明有新的连接
newSock, addr = sock.accept()
print("Connected from: ", addr)
ins.append(newSock)
adrs[newSock] = addr
else:
# 连接套接字上发生 input 事件说明有数据可读,或者是client端断开连接
newData = x.recv(1024)
if newData:
# 有新的数据到来,此时将发给该 client 的响应入队
print("%d bytes from %s" %(len(newData), adrs[x]))
data[x] = data.get(x, "") + newData
if x not in ous:
ous.append(x)
else:
# 连接套接字上发生 input 事件,如果不是有新数据可读,说明是client断开连接
print("Disconnected from: ", adrs[x])
del adrs[x]
try:
ous.remove(x)
except ValueError: pass
x.close()
ins.remove(x)
for x in o: # 处理发生 output 事件的套接字
# 有 output 事件,说明此时可写
tosend = data.get(x)
if tosend:
nsent = x.send(tosend)
print("%d bytes to %s" %(nsent, adrs[x]))
tosend = tosend[nsent:]
if tosend:
print("%d bytes remain for %s" %(len(tosend), adrs[x]))
data[x] = tosend
else:
try:
del data[x]
except KeyError:
pass
ous.remove(x)
print("No data currently remain for:", adrs[x])
finally:
sock.close()

  

Python网络编程(4)——异步编程select & epoll的更多相关文章

  1. .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)

    本文内容 异步编程类型 异步编程模型(APM) 参考资料 首先澄清,异步编程模式(Asynchronous Programming Patterns)与异步编程模型(Asynchronous Prog ...

  2. C#并发编程之异步编程2

    C#并发编程之异步编程(二)   写在前面 前面一篇文章介绍了异步编程的基本内容,同时也简要说明了async和await的一些用法.本篇文章将对async和await这两个关键字进行深入探讨,研究其中 ...

  3. 【憩园】C#并发编程之异步编程(一)

    写在前面 C#5.0中,对异步编程进行了一次革命性的重构,引入了async和await这两个关键字,使得开发人员在不需要深刻了解异步编程的底层原理,就可以写出十分优美而又代码量极少的代码.如果使用得当 ...

  4. C#复习笔记(5)--C#5:简化的异步编程(异步编程的基础知识)

    异步编程的基础知识 C#5推出的async和await关键字使异步编程从表面上来说变得简单了许多,我们只需要了解不多的知识就可以编写出有效的异步代码. 在介绍async和await之前,先介绍一些基础 ...

  5. Java网络编程中异步编程的理解

    目录 前言 一.异步,同步,阻塞和非阻塞的理解 二.异步编程从用户层面和框架层面不同角度的理解 用户角度的理解 框架角度的理解 三.为什么使用异步 四.理解这些能在实际中的应用 六.困惑 参考文章 前 ...

  6. 【憩园】C#并发编程之异步编程(三)

      写在前面 本篇是异步编程系列的第三篇,本来计划第三篇的内容是介绍异步编程中常用的几个方法,但是前两篇写出来后,身边的朋友总是会有其他问题,所以决定再续写一篇,作为异步编程(一)和异步编程(二)的补 ...

  7. 【憩园】C#并发编程之异步编程(二)

    写在前面 前面一篇文章介绍了异步编程的基本内容,同时也简要说明了async和await的一些用法.本篇文章将对async和await这两个关键字进行深入探讨,研究其中的运行机制,实现编码效率与运行效率 ...

  8. C#复习笔记(5)--C#5:简化的异步编程(异步编程的深入分析)

    首先,阐明一下标题的这个“深入分析”起得很惭愧,但是又不知道该起什么名字,这个系列也主要是做一些复习的笔记,供自己以后查阅,如果能够帮助到别人,那自然是再好不过了. 然后,我想说的是异步方法的状态机真 ...

  9. Python网络编程之socket编程

    什么是Socket? Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面 ...

  10. python --------------网络(socket)编程

    一.网络协议 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构(互联网中处处是C/S架构):B/S架构也是C/S架构的一种,B/S是浏览器/服务器 C/S架构与socket的关系: ...

随机推荐

  1. poj3207 2-SAT入门

    一开始题意没读懂 = = 题意:比如说对于表盘上a到b.c到d都要连边,这两个边不能交叉.这两个边要么都在圆内要么都在圆外,而且可以是曲线= = 比如这种情况:(Reference:http://bl ...

  2. HackerRank Extra long factorials

    传送门 今天在HackerRank上翻到一道高精度题,于是乎就写了个高精度的模板,说是模板其实就只有乘法而已. Extra long factorials Authored by vatsalchan ...

  3. 用requests库实现登录遇到的问题

    想登录zhihu,然后总是得到403 foribidden的错误,各种谷歌百度,得到结论说是输入错误或者是url错误,用fldder发现的确是url错了,post的地址是错误的 ==. 开始以为是#s ...

  4. java + jquery + ajax + json 交互

    前端js部分: $.ajax({ async:true, cache:false, type:"POST", dataType : 'json', url:"/shopp ...

  5. 从系统的gallery获取图片

    1 ) 效果演示: 2代码演示 布局代码:

  6. Spring学习3—控制反转(IOC)Spring依赖注入(DI)和控制反转(IOC)

    一.思想理解 Spring 能有效地组织J2EE应用各层的对象.不管是控制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的 管理下有机地协调.运行.S ...

  7. javascript学习随笔(二)原型prototype

    JavaScript三类方法: 1.类方法:2.对象方法:3.原型方法;注意三者异同 例: function People(name){ this.name=name; //对象方法 this.Int ...

  8. Oracle数据分页,并传出数据集

    1.创建Package create or replace package forPaged is type my_csr is ref cursor; procedure getPaged(tabl ...

  9. win10系统安装.net35的命令行方式

    有些程序在windows系统中需要安装.net35才能运行,下载太慢了,可以直接在docs窗口直接安装,命令行如下:Dism /online /enable-feature /featurename: ...

  10. CSS 兼容 总结

    1> margin加倍的问题 设置为float的div在ie下设置的margin会加倍.这是一个ie6都存在的bug. 解决方案是在这个div里面加上display:inline; 例如: &l ...