由于文件夹可能有多层目录,因此需要对其进行递归遍历。
本文采取了简单的协议定制,定义了五条命令,指令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框架的文件夹网络传输源码。
   
 
   