最近在做一个文档的管理系统,用ftp的话 权限 不够,有的目录无权访问。
网上找了一些,都不能够满足业务需要,只有自己来设计一个通信协议来传输文件
设计思路是 切换 服务端和 客户端 的状态来完成文件发送(其实点对点用这个协议也可以)
下面协议测试代码,将D:/book.pdf 发送到D:/1.pdf 后续还需要扩展断点 相关功能
服务端
# -*- coding: gbk -*-
from twisted.internet.protocol import Protocol,Factory
from twisted.protocols.basic import LineReceiver
from pyftpdlib import ftpserver
from twisted.internet import reactor
import pickle
import os
########################################################################
class Status:
RECV='RECV' #接受文件状态
SEND='SEND' #发送文件状态
DONE='DONE' #当前文件传输完成
WAIT='WAIT' #等待对方响应)
LOOP='LOOP' #循环发送字节流
ABORT='ABORT' #终止发送
class FileTranProtocol(LineReceiver):
"""socket文件传输协议"""
#----------------------------------------------------------------------
def __init__(self):
self.fp=None
self.totalsize=0
self.rest=0#这个用来扩展断点功能
self.mystatus=None
self.yourstatus=None
self.blocksize=0
self.localpath=None
self.cursize=0
#根据命令行 查找对应的命令
def lineReceived(self, line):
arr=line.split(':')
cmd=line[0:4]
param=pickle.loads(line[4:])
func=getattr(self,'do_'+cmd)
func(param)
def dataReceived(self,data):
#我处在接受文件 状态 对方处在发送文件状态
if self.mystatus==Status.LOOP:
self.fp.write(data)
self.cursize+=len(data)
#由于我处于LOOP态
#对方处于SEND态 可以接受 Cmd
#这里 用DONE 命令来关闭文件流
if self.cursize>=self.totalsize:
self.DONE()
else:
#处理命令行
LineReceiver.dataReceived(self,data)
def DONE(self):
if self.fp:
self.fp.flush()
self.fp.close()
self.mystatus=Status.DONE
self.sendLine('DONE'+pickle.dumps('EOF'))
def do_DONE(self,param):
if self.fp:
self.fp.flush()
self.fp.close()
self.mystatus=self.yourstatus=Status.DONE
#发送文件 命令
def RETR(self,localpath,blocksize=8192):
self.totalsize=os.path.getsize(localpath)
self.localpath=localpath
self.blocksize=blocksize
self.sendLine('STOR'+pickle.dumps((self.localpath,self.totalsize,self.blocksize)))
self.STAT(Status.WAIT)
#处理 接受文件 命令
def do_STOR(self,param):
self.localpath,self.totalsize,self.blocksize=param[0],param[1],param[2]
#接收方 选择本地保存路径
self.localpath='D:/1.pdf'
self.fp=open(self.localpath,'ab') if self.rest else open(self.localpath,'wb')
self.STAT(Status.RECV)
#告诉对方 我的状态
def STAT(self,status):
print 'tell other '+status
self.mystatus=status
self.sendLine('STAT'+pickle.dumps((status,self.rest)))
def do_STAT(self,param):
print 'tell me '+param[0]
self.yourstatus,self.rest=param[0],int(param[1])
#接受方告诉我们发送字节流
if self.yourstatus==Status.LOOP:
while 1:
buf=self.fp.read(self.blocksize)
if not buf:break
self.transport.write(buf)
#你准备接受完毕
elif self.yourstatus==Status.RECV and self.mystatus==Status.WAIT:
self.fp=open(self.localpath,'rb') #D:/book.pdf
if self.rest and self.rest<self.totalsize:self.fp.seek(self.rest)
#好的我开始发送了
self.STAT(Status.SEND)
#告诉发送端开始文件流循环
elif self.yourstatus==Status.SEND and self.mystatus==Status.RECV:
self.STAT(Status.LOOP)
def connectionLost(self, reason):
if self.fp:
self.fp.close()
#接受文件 命令
def STOR(self,localpath):
self.STAT(Status.WAIT)
def do_EXIT(self):
if self.fp:self.fp.close()
self.STAT(Status.DONE)
self.__init__(self)
########################################################################
class FileTranFactory(Factory):
def buildProtocol(self,addr):
return FileTranProtocol()
if __name__=='__main__':
reactor.listenTCP(8200, FileTranFactory())
reactor.run()
客户端
from twisted.internet.protocol import Protocol,Factory
from twisted.internet.protocol import ClientCreator
from twisted.internet import reactor
import os
from server import *
def Run(p):
p.RETR('D:/book.pdf')
if __name__=='__main__':
creator = ClientCreator(reactor, FileTranProtocol)
d = creator.connectTCP("localhost", 8200)
d.addCallback(Run)
reactor.run()
运行结果