Python网络编程中的select 和 poll I/O复用的简单使用

From: http://www.cnblogs.com/coser/archive/2012/01/06/2315216.html

首先列一下,sellect、poll、epoll三者的区别 
select 
select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。

select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。

select的一个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制。

另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。

poll 
poll在1986年诞生于System V Release 3,它和select在本质上没有多大差别,但是poll没有最大文件描述符数量的限制。

poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。

epoll 
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll,它几乎具备了之前所说的一切优点,被公认为Linux2.6下性能最好的多路I/O就绪通知方法。

epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。

epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在系统调用时复制的开销。

另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

使用 select : 
在python中,select函数是一个对底层操作系统的直接访问的接口。它用来监控sockets、files和pipes,等待IO完成(Waiting for I/O completion)。当有可读、可写或是异常事件产生时,select可以很容易的监控到。 
select.select(rlist, wlist, xlist[, timeout]) 传递三个参数,一个为输入而观察的文件对象列表,一个为输出而观察的文件对象列表和一个观察错误异常的文件列表。第四个是一个可选参数,表示超时秒数。其返回3个tuple,每个tuple都是一个准备好的对象列表,它和前边的参数是一样的顺序。下面,主要结合代码,简单说说select的使用。 
Server端程序: 
1、该程序主要是利用socket进行通信,接收客户端发送过来的数据,然后再发还给客户端。 
2、首先建立一个TCP/IP socket,并将其设为非阻塞,然后进行bind和listen。 
3、通过select函数获取到三种文件列表,分别对每个列表的每个元素进行轮询,对不同socket进行不同的处理,最外层循环直到inputs列表为空为止 
4、当设置timeout参数时,如果发生了超时,select函数会返回三个空列表。 
代码如下(代码中已经有很详细的注释,这里就不过多解释了):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
'''
Created on 2012-1-6
The echo server example from the socket section can be extanded to watche for more than
one connection at a time by using select() .The new version starts out by creating a nonblocking
TCP/IP socket and configuring it to listen on an address
@author: xiaojay
'''
importselect
importsocket
importQueue
#create a socket
server =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(False)
#set option reused
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR  , 1)
server_address=('192.168.1.102',10001)
server.bind(server_address)
server.listen(10)
#sockets from which we except to read
inputs =[server]
#sockets from which we expect to write
outputs =[]
#Outgoing message queues (socket:Queue)
message_queues ={}
#A optional parameter for select is TIMEOUT
timeout =20
whileinputs:
    print"waiting for next event"
    readable , writable , exceptional =select.select(inputs, outputs, inputs, timeout)
    # When timeout reached , select return three empty lists
    ifnot(readable orwritable orexceptional) :
        print"Time out ! "
        break;   
    fors inreadable :
        ifs isserver:
            # A "readable" socket is ready to accept a connection
            connection, client_address =s.accept()
            print"    connection from ", client_address
            connection.setblocking(0)
            inputs.append(connection)
            message_queues[connection] =Queue.Queue()
        else:
            data =s.recv(1024)
            ifdata :
                print" received ", data , "from ",s.getpeername()
                message_queues[s].put(data)
                # Add output channel for response   
                ifs notinoutputs:
                    outputs.append(s)
            else:
                #Interpret empty result as closed connection
                print"  closing", client_address
                ifs inoutputs :
                    outputs.remove(s)
                inputs.remove(s)
                s.close()
                #remove message queue
                delmessage_queues[s]
    fors inwritable:
        try:
            next_msg =message_queues[s].get_nowait()
        exceptQueue.Empty:
            print" ", s.getpeername() , 'queue empty'
            outputs.remove(s)
        else:
            print" sending ", next_msg , " to ", s.getpeername()
            s.send(next_msg)
     
    fors inexceptional:
        print" exception condition on ", s.getpeername()
        #stop listening for input on the connection
        inputs.remove(s)
        ifs inoutputs:
            outputs.remove(s)
        s.close()
        #Remove message queue
        delmessage_queues[s]
                    

Client端程序: 
Client端创建多个socket进行server链接,用于观察使用select函数的server端如何进行处理。 
代码如下(代码中已经有很详细的注释,这里就不过多解释了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
'''
Created on 2012-1-5
The example client program uses some sockets to demonstrate how the server
with select() manages multiple connections at the same time . The client
starts by connecting each TCP/IP socket to the server
@author: peter
'''
importsocket
messages =["This is the message",
            "It will be sent",
            "in parts "]
print"Connect to the server"
server_address =("192.168.1.102",10001)
#Create a TCP/IP sock
socks =[]
fori inrange(10):
    socks.append(socket.socket(socket.AF_INET,socket.SOCK_STREAM))
fors insocks:
    s.connect(server_address)
counter =0
formessage inmessages :
    #Sending message from different sockets
    fors insocks:
        counter+=1
        print"  %s sending %s"%(s.getpeername(),message+" version "+str(counter))
        s.send(message+" version "+str(counter))
    #Read responses on both sockets
    fors insocks:
        data =s.recv(1024)
        print" %s received %s"%(s.getpeername(),data)
        ifnotdata:
            print"closing socket ",s.getpeername()
            s.close()

 

使用Poll:

Server端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
'''
Created on 2012-1-6
The poll function provides similar features to select() , but the underlying implementation is more efficient.
But poll() is not supported under windows .
@author: xiaojay
'''
importsocket
importselect
importQueue
# Create a TCP/IP socket, and then bind and listen
server =socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setblocking(False)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_address =("192.168.1.102", 10001)
print "Starting up on %s port %s"%server_address
server.bind(server_address)
server.listen(5)
message_queues ={}
#The timeout value is represented in milliseconds, instead of seconds.
timeout =1000
# Create a limit for the event
READ_ONLY =( select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR)
READ_WRITE =(READ_ONLY|select.POLLOUT)
# Set up the poller
poller =select.poll()
poller.register(server,READ_ONLY)
#Map file descriptors to socket objects
fd_to_socket ={server.fileno():server,}
whileTrue:
    print"Waiting for the next event"
    events =poller.poll(timeout)
    print"*"*20
    printlen(events)
    printevents
    print"*"*20
    forfd ,flag in events:
        s =fd_to_socket[fd]
        ifflag & (select.POLLIN | select.POLLPRI) :
            ifs isserver :
                # A readable socket is ready to accept a connection
                connection , client_address =s.accept()
                print" Connection ", client_address
                connection.setblocking(False)
                 
                fd_to_socket[connection.fileno()] =connection
                poller.register(connection,READ_ONLY)
                 
                #Give the connection a queue to send data
                message_queues[connection]  =Queue.Queue()
            else:
                data =s.recv(1024)
                ifdata:
                    # A readable client socket has data
                    print"  received %s from %s "%(data, s.getpeername())
                    message_queues[s].put(data)
                    poller.modify(s,READ_WRITE)
                else:
                    # Close the connection
                    print"  closing", s.getpeername()
                    # Stop listening for input on the connection
                    poller.unregister(s)
                    s.close()
                    delmessage_queues[s]
        elifflag & select.POLLHUP :
            #A client that "hang up" , to be closed.
            print" Closing ", s.getpeername() ,"(HUP)"
            poller.unregister(s)
            s.close()
        elifflag & select.POLLOUT :
            #Socket is ready to send data , if there is any to send
            try:
                next_msg =message_queues[s].get_nowait()
            exceptQueue.Empty:
                # No messages waiting so stop checking
                prints.getpeername() , " queue empty"
                poller.modify(s,READ_ONLY)
            else:
                print" sending %s to %s"%(next_msg , s.getpeername())
                s.send(next_msg)
        elifflag & select.POLLERR:
            #Any events with POLLERR cause the server to close the socket
            print"  exception on", s.getpeername()
            poller.unregister(s)
            s.close()
            delmessage_queues[s]

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/403784.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深拷贝与浅拷贝Object.assign()

深拷贝与浅拷贝 Object.assign()会身拷贝一个复杂类型 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title…

TableView的优化

一&#xff1a;什么是TableView的优化以及为什么要优化 1&#xff09;CPU&#xff08;中央处理器&#xff09;和GPU&#xff08;图形处理器&#xff09;&#xff1a;CPU主要从事逻辑计算的一些工作&#xff1b;GPU主要从事图形处理方面的工作。 2&#xff09;CPU和GPU的共同点&…

Python---静态Web服务器-面向对象开发

1. 以面向对象的方式开发静态Web服务器 实现步骤: 把提供服务的Web服务器抽象成一个类(HTTPWebServer)提供Web服务器的初始化方法&#xff0c;在初始化方法里面创建socket对象提供一个开启Web服务器的方法&#xff0c;让Web服务器处理客户端请求操作。 2. 静态Web服务器-面向…

Androidの网络Http之判断是否连接服务器

1.采用Http方式&#xff1a; public boolean isConnByHttp(){boolean isConn false;URL url;HttpURLConnection conn null;try {url new URL("ttp://wl.daishu001.com/YHDriver.asmx");conn (HttpURLConnection)url.openConnection();conn.setConnectTimeout(100…

linux字符设备驱动之字符之异步通知

在前面的博文中记录的都是应用层主动查询读取驱动按键状态。驱动可不可以在有信号之后&#xff0c;主动上报通知应用层事件呢&#xff1f;当然可以&#xff0c;linux如此博大精深。我们使用异步通信机制&#xff0c;signal的办法实现该功能。 所谓的异步&#xff0c;就是进程可…

【Linux开发】linux设备驱动归纳总结(二):模块的相关基础概念

linux设备驱动归纳总结&#xff08;二&#xff09;&#xff1a;模块的相关基础概念 系统平台&#xff1a;Ubuntu 10.04 开发平台&#xff1a;S3C2440开发板 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 一。初探linux内核模块 内…

2020-12-24

console.log()&#xff0c;对于一般对于基本类型number、string、boolean、null、undefined 的输出是可信的。但对于Object 等引用类型来说&#xff0c;则就会出现上述异常打印输出。&#xff08;调试复杂数据类型时候通过打断点来看即可&#xff09; 正常预期 <script>…

XAML实例教程系列 - 资源(Resources)

在Windows 8 Metro应用开发中&#xff0c;XAML主要用于应用界面设计&#xff0c;无论是开发人员还是设计人员经常会设计自定义用户界面或者控件行为&#xff0c;其中会涉及到不同方面的代码设计&#xff0c;例如控件模板&#xff0c;控件样式&#xff0c;动画设计等。为了方便设…

LCD显示深究day01 mmap知识补充

<div class"container clearfix"><main><article><h1 class"csdn_top">Linux中的mmap的使用</h1><div class"article_bar clearfix"><div class"artical_tag"><span class"origina…

Python 多线程抓取网页

From: http://www.cnblogs.com/coser/archive/2012/03/16/2402389.html 最近&#xff0c;一直在做网络爬虫相关的东西。 看了一下开源C写的larbin爬虫&#xff0c;仔细阅读了里面的设计思想和一些关键技术的实现。 1、larbin的URL去重用的很高效的bloom filter算法&#xff1…

解决vue的滚动条监听事件无效 解决vue的滚动条scrollTop距离总是为0无效问题

话不多说 直接上代码&#xff08;方法可以直接复制拿去&#xff0c; html部分需要改成你的元素的ref和点击回到顶部的方法名称&#xff09; html <section ref"scrollbox" class"inner-body"><div>这里放了很多内容 出现了滚动条</div&g…

动态规划练习 13

题目&#xff1a;Longest Ordered Subsequence (POJ 2533) 链接&#xff1a;http://acm.pku.edu.cn/JudgeOnline/problem?id2533 #include <iostream> #include <vector> using namespace std; int LIS(const vector<int> &data) { vector<int> n…

奔跑吧Linux内核初识

断更新博客有一段时间了。入职两年了一家创业公司&#xff0c;那是真心的累&#xff0c;当然了获得了技术上很大的提升。搞了两年的vr产品&#xff0c;唯一遗憾的是&#xff0c;平台是ST单片机&#xff0c;远离了系统级别的知识。回看刚出校园时的三年计划&#xff0c;和第一年…

装载问题

1、回溯法 (1)描述:回溯法是一种选优搜索法&#xff0c;按选优条件向前搜索&#xff0c;以达到目标。但当探索到某一步时&#xff0c;发现原先选择并不优或达不到目标&#xff0c;就退回一步重新选择&#xff0c;这种走不通就退回再走的技术为回溯法。 (2)原理: 回溯法在问题的…

android开发(13) 尝试在流布局中移动控件

我们常用的linearlayout,等都属于流布局&#xff0c;在流布局中如何移动控件呢&#xff1f; 我决定做个尝试。虽然可以使用绝对布局&#xff0c;但我不倾向使用这个布局。那么看看我的方式吧。 记得margin这个属性吗&#xff0c;我们就用来它来控制控件的位置&#xff0c;改动它…

阴影 border: 0 0 0 1px #4caaff;

阴影 border: 0 0 0 1px #4caaff;

第一章 处理器体系结构

1.请简述精简指令集RISC和复杂指令集CISC的区别 2.请简述数值 0x123456789 在大小端字节序处理器的存储器中的存储方式 3.请简述在你所熟悉的处理器&#xff08;比如双核Cortex-A9&#xff09;中一条存储读写指令的执行全过程 4.请简述内存屏障&#xff08;memory barrier&am…

Linux双网卡绑定实现

概述&#xff1a;通过网卡绑定&#xff0c;处理网卡单点故障&#xff0c;实验的操作系统是Redhat Linux Enterprise 5.3.绑定的前提条件&#xff1a;芯片组型号相同&#xff0c;而且网卡应该具备自己独立的BIOS芯片。网卡绑定时有四种模式&#xff0c;其中常用的是模式0和模式1…

给element的select添加复选框

需求&#xff1a;要求给select多选的时候&#xff0c;给下拉框前加上复选框样式 element select原样式 需要更改后的样式 html <el-selectv-model"searchObj.knowledgeIds"class"select-box"filterablemultiplecollapse-tagsstyle"margin-left…

TCP选项:TCP_NODELAY和TCP_CORK

From: http://blog.163.com/zhangjie_0303/blog/static/990827062012718316231/ Nagle算法 TCP_NODELAY和TCP_CORK Nagle算法 根据创建者John Nagle命名。该算法用于对缓冲区内的一定数量的消息进行自动连接。该处理过程 (称为Nagling)&#xff0c;通过减少必须发送的封包的…