由于文件夾可能有多層目錄,因此需要對其進行遞歸遍歷。
本文采取了簡單的協議定制,定義了五條命令,指令Head如下:
Sync:標識開始同步文件夾
End:標識結束同步
File:標識傳輸的文件名(相對路徑)
Folder:標志文件夾(相對路徑)
None:文件內容
每條命令以CMB_BEGIN開始,以CMB_END結束。
客戶端需要對接收緩沖做解析,取出一條一條的指令,然后根據指令的Head做相應的處理,比如創建文件夾、寫入文件等。
下面是服務端的代碼:
from twisted.internet import reactorfrom twisted.internet.protocol import Protocol,Factoryfrom twisted.protocols.basic import LineReceiverimport osimport struct BUFSIZE = 4096 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, line): print line self.transport.write("Hello Client, I am the Server!/r/n") self.transport.write("CMB_BEGIN") self.transport.write("Sync") self.transport.write("CMB_END") self.send_file_folder('server') def send_file_folder(self,folder): '''send folder to the client''' for f in os.listdir(folder): sourceF = os.path.join(folder, f) if os.path.isfile(sourceF): print 'File:',sourceF[7:] self.transport.write("CMB_BEGIN") self.transport.write("File:" + sourceF[7:]) self.transport.write("CMB_END") fp = open(sourceF,'rb') while 1: filedata = fp.read(BUFSIZE) if not filedata: break else: self.transport.write("CMB_BEGIN") self.transport.write(filedata) print 'send size:::::::::',len(filedata) self.transport.write("CMB_END") fp.close() self.transport.write("CMB_BEGIN") self.transport.write("End") self.transport.write("CMB_END") if os.path.isdir(sourceF): print 'Folder:',sourceF[7:] self.transport.write("CMB_BEGIN") self.transport.write("Folder:" + sourceF[7:]) self.transport.write("CMB_END") self.send_file_folder(sourceF) factory = Factory()factory.protocol = SimpleLoggerreactor.listenTCP(1234, factory)reactor.run()Server在收到Client的某個信號之后(此代碼中,當Client隨便向Server發送任何內容都可),Server即會調用send_file_folder將sever文件夾下的內容全部發送給客戶端。
服務端運行結果如下:

下面是客戶端的代碼:
from twisted.internet.selectreactor import SelectReactorfrom twisted.internet.protocol import Protocol,ClientFactoryfrom twisted.protocols.basic import LineReceiverimport osfrom struct import * reactor = SelectReactor()protocol = Protocol()prepare = 0filename = ""sourceDir = 'client'recvBuffer = '' def delete_file_folder(src): '''delete files and folders''' if os.path.isfile(src): try: os.remove(src) except: pass elif os.path.isdir(src): for item in os.listdir(src): itemsrc = os.path.join(src,item) delete_file_folder(itemsrc) try: os.rmdir(src) except: pass def clean_file_folder(src): '''delete files and child folders''' delete_file_folder(src) os.mkdir(src) def writefile(filename,data): print 'write file size:::::::::',len(data) fp = open(filename,'a+b') fp.write(data) fp.close() class QuickDisconnectedProtocol(Protocol): def connectionMade(self): print "Connected to %s."%self.transport.getPeer().host self.transport.write("Hello server, I am the client!/r/n") def dataReceived(self, line): global prepare global filename global sourceDir global recvBuffer recvBuffer = recvBuffer + line self.processRecvBuffer() def processRecvBuffer(self): global prepare global filename global sourceDir global recvBuffer while len(recvBuffer) > 0 : index1 = recvBuffer.find('CMB_BEGIN') index2 = recvBuffer.find('CMB_END') if index1 >= 0 and index2 >= 0: line = recvBuffer[index1+9:index2] recvBuffer = recvBuffer[index2+7:] if line == 'Sync': clean_file_folder(sourceDir) if line[0:3] == "End": prepare = 0 elif line[0:5] == "File:": name = line[5:] filename = os.path.join(sourceDir, name) print 'mk file:',filename prepare = 1 elif line[0:7] == "Folder:": name = line[7:] filename = os.path.join(sourceDir, name) print 'mkdir:',filename os.mkdir(filename) elif prepare == 1: writefile(filename,line) else: break class BasicClientFactory(ClientFactory): protocol=QuickDisconnectedProtocol def clientConnectionLost(self,connector,reason): print 'Lost connection: %s'%reason.getErrorMessage() reactor.stop() def clientConnectionFailed(self,connector,reason): print 'Connection failed: %s'%reason.getErrorMessage() reactor.stop() reactor.connectTCP('localhost',1234,BasicClientFactory())reactor.run()客戶端提取出來自Server的指令,當提取出Sync指令時,則將sourceDir目錄清空,然后根據后續的指令,跟Server的文件夾進行同步。
客戶端運行結果如下:

需要注意的地方:
Client寫入文件時,需要以二進制的方式打開文件,否則,在傳輸二進制文件時可能出現錯誤或導致文件損壞。
經過測試,代碼可以正常的運行,文件夾同步成功,文本文件、圖像和其他類型的二進制文件均可正常傳輸。
新聞熱點
疑難解答