由于文件夹可能有多层目录,因此需要对其进行递归遍历。
本文采取了简单的协议定制,定义了五条命令,指令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写入文件时,需要以二进制的方式打开文件,否则,在传输二进制文件时可能出现错误或导致文件损坏。
经过测试,代码可以正常的运行,文件夹同步成功,文本文件、图像和其他类型的二进制文件均可正常传输。