鍍金池/ 教程/ Python/ python 實(shí)現(xiàn) select 和 epoll 模型 socket 網(wǎng)絡(luò)編程
通過 memcached 實(shí)現(xiàn)領(lǐng)號(hào)排隊(duì)功能及 python 隊(duì)列實(shí)例
利用 pypy 提高 python 腳本的執(zhí)行速度及測(cè)試性能
Python FAQ3-python 中 的原始(raw)字符串
Mongodb 千萬級(jí)數(shù)據(jù)在 python 下的綜合壓力測(cè)試及應(yīng)用探討
Parallel Python 實(shí)現(xiàn)程序的并行多 cpu 多核利用【pp 模塊】
python simplejson 模塊淺談
服務(wù)端 socket 開發(fā)之多線程和 gevent 框架并發(fā)測(cè)試[python 語(yǔ)言]
python Howto 之 logging 模塊
python 之 MySQLdb 庫(kù)的使用
關(guān)于 python 調(diào)用 zabbix api 接口的自動(dòng)化實(shí)例 [結(jié)合 saltstack]
python 之利用 PIL 庫(kù)實(shí)現(xiàn)頁(yè)面的圖片驗(yàn)證碼及縮略圖
Python 通過 amqp 消息隊(duì)列協(xié)議中的 Qpid 實(shí)現(xiàn)數(shù)據(jù)通信
python 中用 string.maketrans 和 translate 巧妙替換字符串
python linecache 模塊讀取文件用法詳解
Python 批量更新 nginx 配置文件
python 計(jì)算文件的行數(shù)和讀取某一行內(nèi)容的實(shí)現(xiàn)方法
python+Django 實(shí)現(xiàn) Nagios 自動(dòng)化添加監(jiān)控項(xiàng)目
多套方案來提高 python web 框架的并發(fā)處理能力
python 寫報(bào)警程序中的聲音實(shí)現(xiàn) winsound
python 調(diào)用 zabbix 的 api 接口添加主機(jī)、查詢組、主機(jī)、模板
對(duì) Python-memcache 分布式散列和調(diào)用的實(shí)現(xiàn)
使用 python 構(gòu)建基于 hadoop 的 mapreduce 日志分析平臺(tái)
一個(gè)腳本講述 python 語(yǔ)言的基礎(chǔ)規(guī)范,適合初學(xué)者
Python 編寫的 socket 服務(wù)器和客戶端
如何將 Mac OS X10.9 下的 Python2.7 升級(jí)到最新的 Python3.3
python 監(jiān)控文件或目錄變化
報(bào)警監(jiān)控平臺(tái)擴(kuò)展功能 url 回調(diào)的設(shè)計(jì)及應(yīng)用 [python 語(yǔ)言]
Python 處理 cassandra 升級(jí)后的回滾腳本
python 實(shí)現(xiàn) select 和 epoll 模型 socket 網(wǎng)絡(luò)編程
關(guān)于 B+tree (附 python 模擬代碼)
通過 python 和 websocket 構(gòu)建實(shí)時(shí)通信系統(tǒng)[擴(kuò)展 saltstack 監(jiān)控]

python 實(shí)現(xiàn) select 和 epoll 模型 socket 網(wǎng)絡(luò)編程

這里簡(jiǎn)單搞搞 select 和 eopll 的接口開發(fā) ~

select 目前幾乎在所有的平臺(tái)上支持,其良好跨平臺(tái)支持也是它的一個(gè)優(yōu)點(diǎn),事實(shí)上從現(xiàn)在看來,這也是它所剩不多的優(yōu)點(diǎn)之一,現(xiàn)在其實(shí)更多的人用 epoll,在 python 下 epoll 文檔有點(diǎn)少,就先講究搞搞 select ~

select 的一個(gè)缺點(diǎn)在于單個(gè)進(jìn)程能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,在 Linux 上一般為 1024,不過可以通過修改宏定義甚至重新編譯內(nèi)核的方式提升這一限制。

說點(diǎn)我的理解,要是用煩了多線程的網(wǎng)絡(luò)編程,可以試試 select 的模型。

傳遞給 select 的參數(shù)是幾個(gè)列表,分別表示讀事件、寫事件和錯(cuò)誤事件。select 方法返回三個(gè)列表,其中包含滿足條件的對(duì)象(讀、寫和異常)。

服務(wù)端的代碼:

#coding:utf-8
import socket,select
import time
import os
#xiaorui.cc
host = "localhost"
port = 50000
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((host,port))
s.listen(5)
while 1:
     infds,outfds,errfds = select.select([s,],[],[],5)
     if len(infds) != 0:
        clientsock,clientaddr = s.accept()
        buf = clientsock.recv(8196)
        if len(buf) != 0:
            print (buf)
            os.popen('sleep 10').read()                                                                                                      
        clientsock.close()
#     print "no data coming"

客戶端的代碼:

#coding:utf-8
import socket,select
#xiaorui.cc
host = "localhost"
port = 50000
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,port))

s.send("coming from select client")
s.close()

http://wiki.jikexueyuan.com/project/python-actual-combat/images/73.jpg" alt="pic" />

一個(gè)完成的 select 的例子:

這里有隊(duì)列的概念

#
import select                                                                                                                                
import socket
import Queue
import time
import os
#創(chuàng)建 socket 套接字
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.setblocking(False)
#配置參數(shù)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR  , 1)
server_address= ('192.168.0.101',9999)
server.bind(server_address)
server.listen(10)
inputs = [server]
outputs = []
message_queues = {}
#timeout = 20
while inputs:
    print "waiting for next event"
#    readable , writable , exceptional = select.select(inputs, outputs, inputs, timeout)  最后一個(gè)是超時(shí),當(dāng)前連接要是超過這個(gè)時(shí)間的話,就會(huì) kill
    readable , writable , exceptional = select.select(inputs, outputs, inputs)

    # When timeout reached , select return three empty lists
    if not (readable or writable or exceptional) :
        print "Time out ! "
        break;
    for s in readable :
        if s is server:
            #通過 inputs 查看是否有客戶端來
            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)
            if data :
                print " received " , data , "from ",s.getpeername()
                message_queues[s].put(data)
                # Add output channel for response
                if s not in outputs:
                    outputs.append(s)
            else:
                #Interpret empty result as closed connection
                print "  closing", client_address
                if s in outputs :
                    outputs.remove(s)
                inputs.remove(s)
                s.close()
                #清除隊(duì)列信息
                del message_queues[s]
    for s in writable:
        try:
            next_msg = message_queues[s].get_nowait()
        except Queue.Empty:
            print " " , s.getpeername() , 'queue empty'
            outputs.remove(s)
        else:
            print " sending " , next_msg , " to ", s.getpeername()
            os.popen('sleep 5').read()
            s.send(next_msg)

    for s in exceptional:
        print " exception condition on ", s.getpeername()
        #stop listening for input on the connection
        inputs.remove(s)
        if s in outputs:
            outputs.remove(s)
        s.close()
        #清除隊(duì)列信息
        del message_queues[s]

關(guān)于 epoll 的方面,大家可以看看這個(gè)老外的文檔,寫不錯(cuò) ~

select 是輪詢、epoll 是觸發(fā)式的,所以 epoll 的效率高。

參考的文檔地址:http://scotdoyle.com/python-epoll-howto.html

下面是用 epoll 實(shí)現(xiàn)一個(gè)服務(wù)端 ~

blog from xiaorui.cc

import socket, select
EOL1 = b'\n\n'
EOL2 = b'\n\r\n'
response  = b'HTTP/1.0 200 OK\r\nDate: Mon, 1 Jan 1996 01:01:01 GMT\r\n'
response += b'Content-Type: text/plain\r\nContent-Length: 13\r\n\r\n'
response += b'Hello, world!'
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(('0.0.0.0', 8080))
serversocket.listen(1)
serversocket.setblocking(0)
epoll = select.epoll()
epoll.register(serversocket.fileno(), select.EPOLLIN)
try:
   connections = {}; requests = {}; responses = {}
   while True:
      events = epoll.poll(1)
      for fileno, event in events:
         if fileno == serversocket.fileno():
            connection, address = serversocket.accept()
            connection.setblocking(0)
            epoll.register(connection.fileno(), select.EPOLLIN)
            connections[connection.fileno()] = connection
            requests[connection.fileno()] = b''
            responses[connection.fileno()] = response
         elif event & select.EPOLLIN:
            requests[fileno] += connections[fileno].recv(1024)
            if EOL1 in requests[fileno] or EOL2 in requests[fileno]:
               epoll.modify(fileno, select.EPOLLOUT)
               connections[fileno].setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 1)
               print('-'*40 + '\n' + requests[fileno].decode()[:-2])
         elif event & select.EPOLLOUT:
            byteswritten = connections[fileno].send(responses[fileno])
            responses[fileno] = responses[fileno][byteswritten:]
            if len(responses[fileno]) == 0:
               connections[fileno].setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 0)
               epoll.modify(fileno, 0)
               connections[fileno].shutdown(socket.SHUT_RDWR)
         elif event & select.EPOLLHUP:
            epoll.unregister(fileno)
            connections[fileno].close()
            del connections[fileno]
finally:
   epoll.unregister(serversocket.fileno())

Epoll 的最大好處是不會(huì)隨著FD的數(shù)目增長(zhǎng)而降低效率,在 select 中采用輪詢處理,每個(gè) fd 的處理情況,而 epoll 是維護(hù)一個(gè)隊(duì)列,直接看隊(duì)列是不是空就可以了。

在這里也推薦大家用 epoll 寫服務(wù)端的東西,當(dāng)然我自己理解的不夠好,咱們多交流 ?。。?/p>

本文出自 “峰云,就她了?!?博客,謝絕轉(zhuǎn)載!