博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python网络编程(4)——异步编程select & epoll
阅读量:4515 次
发布时间:2019-06-08

本文共 4095 字,大约阅读时间需要 13 分钟。

  在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 socketimport selectsock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.bind(('127.0.0.1', 4424))sock.listen(10)ins = [sock, ]ous = []# socket -> data to senddata = {}# socket -> client address, which is (host, port) tupleadrs = {}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()

  

 

转载于:https://www.cnblogs.com/Security-Darren/p/4746230.html

你可能感兴趣的文章
从“智猪博弈”看所谓“大国责任”
查看>>
Day3:Spring-JDBC、事务管理
查看>>
模块的四种形式
查看>>
Jmeter属性和变量
查看>>
java并发编程:并发容器之CopyOnWriteArrayList(转)
查看>>
python基础——面向对象进阶下
查看>>
Linux vi 命令详解
查看>>
本地如何搭建IPv6环境测试你的APP
查看>>
C++ NULL与nullptr的区别
查看>>
Discretized Streams, 离散化的流数据处理
查看>>
Spark源码分析 – SchedulerBackend
查看>>
python字符串处理
查看>>
live555学习笔记4-计划任务(TaskScheduler)深入探讨
查看>>
Python虚拟机函数机制之名字空间(二)
查看>>
线段树
查看>>
SharePoint2010联合搜索——Google、百度
查看>>
php静态
查看>>
python基础之文件操作
查看>>
在eclipse里头用checkstyle检查项目出现 File contains tab characters (this is the first instance)原因...
查看>>
个人github链接及git学习心得总结
查看>>