標籤:
最近在學習python-twisted庫,之前做非同步並發編程一直都是使用c++,比如linux下的epoll機制,windows的IOCP機制,到後來經常使用的Boost::Asio庫,比較搞的是c++上的非同步經驗反而有點阻礙我開始學習twisted庫,原因如下:
不管是epoll還是IOCP,都是當讀操作或者寫操作可執行時(IOCP則是完成時)會得到一個通知,然後可以執行自己的下一步代碼。這種情景下如果你是做較大資料的收發的話,就可以在得到通知時進行必要的處理後繼續發送或者接受下一個資料區塊,但是twisted的庫則不是這樣設計的,以twisted.protocols.basic.LineReceiver為例,當非同步收到資料後,該類的lineReceived函數或者rawDataReceived(取決於當前的模式)會被觸發,但是它並不包含非同步寫函數,也就是說你無法這樣操作:
1、調用非同步發送函數發送資料
2、資料發送完成時觸發一個回呼函數
3、在該回呼函數中你決定後續的操作,比如繼續發送下一塊資料
那麼twisted的解決方案是什嗎?答案是producer/consumer ,producer負責源源不斷的產生資料(如不斷從file中讀取資料),consumer負責處理資料(通過socket發送出去),當consumer在發送時緩衝滿了它會通知producer暫停提供資料,當緩衝清空後繼續通知producer產生資料(這就類似我們上面提到的,當非同步發送完成時調用一個回呼函數),這個的過程不需要我們在代碼中顯示的控制。由於檔案發送應用的普遍性,twisted提供了一個檔案發送類FileSender,好啦,描述性的內容就到這裡,後續關於producer/consumer我會進一步介紹,下面直接上代碼
from twisted.internet.protocol import Protocol,Factoryfrom twisted.protocols.basic import LineReceiverfrom twisted.protocols.basic import FileSenderfrom twisted.internet.defer import Deferredimport pickle,structimport multiprocessing.poolimport os,jsonclass TransferFileProtocol(LineReceiver): port_ = 0 def __init__(self): self.handle_f_ = None #-------------------------- self.instruction_ = None self.command_ = None self.size_remain_ = 0 #self.setRawMode() def _monitor(self,data): self.size_remain_-=len(data) return data def cbTransferOver(self, lastSent): print(‘download over!‘) self.transport.loseConnection() def connectionMade(self): print(‘Got Connection from ‘,self.transport.client) print ‘work on port:%d‘ % (self.port_,) def connectionLost(self,reason): print(self.transport.client, ‘ disconnected!‘) if self.handle_f_ != None: self.handle_f_.close() if self.size_remain_ != 0: print ‘transfer file fail!‘# if self.command_ == ‘put‘:# os. def lineReceived(self, line): self.instruction_ = json.loads(line) self.command_ = self.instruction_[‘command‘] name_file = self.instruction_[‘name_file‘] if self.command_ == ‘put‘: self.size_remain_ = self.instruction_[‘size_file‘] try: self.handle_f_ = open(‘files/%s‘ % (name_file,),‘wb‘) except: print ‘open %s fail!‘ % (name_file,) self.handle_f_ = None# self.transfer.loseConnection() else: self.setRawMode() elif ‘get‘ == self.command_: self.size_remain_ = 0 if os.path.exists(‘files/%s‘ % (name_file,)): self.size_remain_ = os.stat(‘files/%s‘ % (name_file,)).st_size instruction = dict(size_file=self.size_remain_) if self.size_remain_>0: try: self.handle_f_ = open(‘files/%s‘ % (name_file,),‘rb‘) except: self.size_remain_ = 0 self.handle_f_ = None else: self.setRawMode() self.transport.write(json.dumps(instruction)+‘\r\n‘) if self.size_remain_>0: sender = FileSender() sender.CHUNCK_SIZE = 2**16 d = sender.beginFileTransfer(self.handle_f_,self.transport,self._monitor) d.addCallback(self.cbTransferOver) def rawDataReceived(self, data): print ‘length of data:%d‘ % (len(data),) if ‘put‘ == self.command_ and self.handle_f_ != None: self.handle_f_.write(data) self.size_remain_ -= len(data)def recv_func(port): from twisted.internet import epollreactor epollreactor.install() from twisted.internet import reactor TransferFileProtocol.port_ = port factory = Factory() factory.protocol = TransferFileProtocol reactor.listenTCP(port,factory) reactor.run()if __name__ ==‘__main__‘: ports = [6200,6202,6204,6206] pool = multiprocessing.pool.Pool(len(ports)) pool.map(recv_func,ports)# recv_func(6200)
利用python-twisted庫實現一個檔案收發服務