python:非阻塞或非同步編程

來源:互聯網
上載者:User

例如,對於一個聊天室來說,因為有多個串連需要同時被處理,所以很顯然,阻塞或同步的方法是不合適的,這就像買票只開了一個視窗,佷多人排隊等一樣。那麼我們如何解決這個問題呢?主要有三種方法:forking、threading、非同步I/O。

Forking和threading的方法非常簡單,通過使用SocketServer服務類的min-in類就可以實現。forking只適用於類Unix平台;threading需要注意記憶體共用的問題。
非同步I/O如果底層的方法來實現是有點困難的。要簡單點,我們可以考慮使用標準庫中的架構或Twisted(Twisted是一個非常強大的非同步網路編程的架構)。

一、用ScoketServer實現Forking和threading

下面我們使用兩個例子來分別建立forking伺服器和threading伺服器。

Forking 伺服器

from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler

class Server(ForkingMixIn, TCPServer): pass

class Handler(StreamRequestHandler):
    def handle(self):
        addr = self.request.getpeername()
        print 'Got connection from', addr
        self.wfile.write('Thank you for connecting')

server = Server(('', 1234), Handler)
server.serve_forever()

threading伺服器

from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler

class Server(ThreadingMixIn, TCPServer): pass

class Handler(StreamRequestHandler):
    def handle(self):
        addr = self.request.getpeername()
        print 'Got connection from', addr
        self.wfile.write('Thank you for connecting')

server = Server(('', 1234), Handler)
server.serve_forever()

二、使用select實現非同步I/O

所謂非同步I/O,打個比方,就是如果一大群人都想你聽他說話,那麼你就給他們每人一分鐘的時間說,大家輪流說,沒說完的待會兒輪到時再繼續說。也就是一個時間片的方法。

要實現非同步I/O,我們可以通過使用架構asyncore/asynchat或Twisted,它們都是基於select函數或poll函數(poll只適於類Unix系統)的。select和poll函數都來自select模組。

select 函數要求三個必須序列作為參數和一個可選的以秒為單位的逾時值。序列中是表示檔案描述符的整數值,它們是我們要等待的串連。這三個序列是關於輸入、輸出和 異常條件的。如果逾時值沒有給出的話,select將處於阻塞狀態(也就是等待)直到有檔案描述符準備動作。如果逾時值給出了,那麼select只阻塞給 定的時間。如果逾時值是0的話,那麼將不阻塞。select返回的值是一個由三個序列組成的元組,它們分別代表相應參數的活動的子集。例如,第一個序列返 回的是用於讀的輸入檔案描述符構成的序列。

序列可以包含檔案對象(不適於Windows)或socket。下面這個例子建立一個使用 select去服務幾個已連線的服務器(注意:服務端的socket自身也提供給了select,以便於它能夠在有新的串連準備接受時發出訊號通知)。這個 伺服器只是簡單地列印接受自用戶端的資料。你可以使用telnet(或寫一個基於socket的簡單的用戶端)來串連測試它。

select server

import socket, select

s = socket.socket()
host = socket.gethostname()
port = 1234
s.bind((host, port))

s.listen(5)
inputs = [s]
while True:
    rs, ws, es = select.select(inputs, [], [])
    for r in rs:
        if r is s:
            c, addr = s.accept()
            print 'Got connection from', addr
            inputs.append(c)
        else:
            try:
                data = r.recv(1024)
                disconnected = not data
            except socket.error:
                disconnected = True

            if disconnected:
                print r.getpeername(), 'disconnected'
                inputs.remove(r)
            else:
                print data

三、Twisted

Twisted 是針對Python的一個事件驅動的網路架構,最初是為了網路遊戲而開發的,但是現在被應用於各類網路軟體。用Twisted,你可以實現事件處理器,非 常類似用GUI工具包(Tk, GTK, Qt, wxWidgets)。這部分我將介紹一些基本的概念和示範如何使用Twisted來做一些相對簡單的 網路編程。Twisted是非常強大的架構並提供了大量的支援,如:Web伺服器和用戶端、 SSH2, SMTP, POP3, IMAP4, AIM, ICQ, IRC, MSN,Jabber, NNTP, DNS等等。

早先我們所寫的基於socket的伺服器,它們都有一個顯示的事件迴圈:尋找新的串連和新的資料;基於SocketServer的伺服器有一個隱含的迴圈:尋找串連和為串連建立處理器。但時處理器仍然時顯示的讀資料。

而 Twisted使用了更多的基於事件的方式。要寫一個基本的伺服器,你要實現事件處理器,它處理諸如一個新的用戶端串連、新的資料到達和用戶端串連中斷等 情況。在Twisted中,你的事件處理器定義在一個protocol中;你也需要一個factory,當一個新的串連到達時它能夠構造這個 protocol對象,但是如果你僅僅想建立一個自訂的Protocol類的執行個體的話,你可以使用來自Twisted的factory,Factory 類在模組twisted.internet.protocol中。當你寫你的protocol時,使用
twisted.internet.protocol模組中的Protocol作為你的父類。當你得到一個串連時,事件處理器 connectionMade被調用;當你丟失了一個串連時,connectionLost被調用。從用戶端接受資料使用處理器 dataReceived。但是你不能使用事件處理策略向用戶端發送資料;要向用戶端發送資料,你可以使用self.transport,它有一個 write方法。它也有一個client屬性,其中包含了用戶端的地址(主機名稱和連接埠)。

下面這個例子是一個Twisted版的伺服器。 其中執行個體化了Factory並設定了它的protocol屬性以便它知道使用哪個protocol與用戶端通訊(這就是所謂的你的自訂 protocol)。然後你使用factory開始監聽指定的連接埠,factory通過執行個體化的protocol對象處理串連。監聽使用reactor模 塊中的listenTCP函數。最後,你通過調用reactor模組中的run函數來開始伺服器。

from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory

# 定義你Protocol類
class SimpleLogger(Protocol):

    def connectionMade(self):
        print 'Got connection from', self.transport.client
    def connectionLost(self, reason):
        print self.transport.client, 'disconnected'
    def dataReceived(self, data):
        print data

# 執行個體化Factory

factory = Factory()

# 設定factory的protocol屬性以便它知道使用哪個protocol與用戶端通訊(這就是所謂的你的自訂
# protocol)

factory.protocol = SimpleLogger

# 監聽指定的連接埠

reactor.listenTCP(1234, factory)

# 開始運行主程式
reactor.run()

為 你的處理目的而寫一個自訂的protocol是很容易的。模組twisted.protocols.basic中包含了幾個有用的已存在的 protocol,其中的LineReceiver執行dataReceived並在接受到了一個完整的行時呼叫事件處理器lineReceived。如 果當你在接受資料時除了使用lineReceived,還要做些別的,那麼你可以使用LineReceiver定義的名為rawDataReceived 事件處理器。下面是一使用LineReceiver的伺服器例子:

from twisted.internet import reactor
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver

class SimpleLogger(LineReceiver):

    def connectionMade(self):
        print 'Got connection from', self.transport.client
    def connectionLost(self, reason):
        print self.transport.client, 'disconnected'
    def lineReceived(self, line):
        print line

factory = Factory()
factory.protocol = SimpleLogger
reactor.listenTCP(1234, factory)
reactor.run()

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.