本文采取了简单的协议定制,定义了五条命令,指令head如下:
sync:标识开始同步文件夹
end:标识结束同步
file:标识传输的文件名(相对路径)
folder:标志文件夹(相对路径)
none:文件内容
每条命令以cmb_begin开始,以cmb_end结束。
客户端需要对接收缓冲做解析,取出一条一条的指令,然后根据指令的head做相应的处理,比如创建文件夹、写入文件等。
下面是服务端的代码:
from twisted.internet import reactor from twisted.internet.protocol import protocol,factory from twisted.protocols.basic import linereceiver import os import 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 = simplelogger reactor.listentcp(1234, factory) reactor.run()
server在收到client的某个信号之后(此代码中,当client随便向server发送任何内容都可),server即会调用send_file_folder将sever文件夹下的内容全部发送给客户端。
服务端运行结果如下:
下面是客户端的代码:
from twisted.internet.selectreactor import selectreactor from twisted.internet.protocol import protocol,clientfactory from twisted.protocols.basic import linereceiver import os from struct import * reactor = selectreactor() protocol = protocol() prepare = 0 filename = "" 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写入文件时,需要以二进制的方式打开文件,否则,在传输二进制文件时可能出现错误或导致文件损坏。
经过测试,代码可以正常的运行,文件夹同步成功,文本文件、图像和其他类型的二进制文件均可正常传输。
更多python 基于twisted框架的文件夹网络传输源码。