由于文件夹可能有多层目录,因此需要对其进行递归遍历。
本文采取了简单的协议定制,定义了五条命令,指令Head如下:
Sync:标识开始同步文件夹
End:标识结束同步
File:标识传输的文件名(相对路径)
Folder:标志文件夹(相对路径)
None:文件内容
每条命令以CMB_BEGIN开始,以CMB_END结束。
客户端需要对接收缓冲做解析,取出一条一条的指令,然后根据指令的Head做相应的处理,比如创建文件夹、写入文件等。
下面是服务端的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
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文件夹下的内容全部发送给客户端。
服务端运行结果如下:
下面是客户端的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
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写入文件时,需要以二进制的方式打开文件,否则,在传输二进制文件时可能出现错误或导致文件损坏。
经过测试,代码可以正常的运行,文件夹同步成功,文本文件、图像和其他类型的二进制文件均可正常传输。
原文链接:http://www.icodelogic.com/?p=516