....................
</td>
</tr>
</tbody>
</table>
Socket、thread、os、time簡單介紹和使用
Socket:
本程序中用到的socket功能很簡單,包括創建socket,監聽,接受連接,連接文件化,發送接收數據,關閉連接。詳看主要部件的介紹部分,或參考其他資料,這里不多說。
Thread:
本程序中只用到start_new_thread(func,(args)),就像看到的,該函數接收兩個參數,一個是希望新線程中執行的函數,還有就是希望給該函數傳入的參數。
這是一個輕量級的開啟線程的方法,更好的做法是繼承線程類,把一個類做成線程,有自己的資源可以訪問。
Os:
本文中主要涉及到的是,os.chdir(),os.getcwd(),os.mkdir()等等,這些關于變更目錄的操作
Time:
參考了pyftpdlib中對時間的處理,不多,建議參考介紹時間庫的文章詳看。
實現思路
首先創建一個主socket綁定到21端口。
然后以一個while循環接受用戶的連接,每接受一個連接就開啟一個新線程與該用戶交互。
在線程中又以一個while循環來接收用戶命令,每接到一條命令,就對用戶的命令和傳遞參數進行解析,并調用對應的handler函數處理。
如遇到pasv操作,則在handler_pas中建立新的socket并返回給客戶端。
大致思路如此。詳看代碼部分。
代碼部分
#--coding:utf8--
import time
import socket,sys
from thread import *
import os
author = 'ksp'
class FTPs():
def init(self,localip='127.0.0.1',path='c:/'):#接受本機ip以綁定socket,接受開放的目錄
self.s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
#這是傳輸時分片的大小
self.PSIZE=4096
self.lip=localip
#綁定到FTP專用端口,21
try:
self.s.bind((self.lip,21))
except:
print 'ip error'
raise
self.path=path
#將工作目錄改變到所設目錄下
try:
os.chdir(path)
except ValueError:
print 'path Invalid'
raise
self.close=False
#文件屬性中的日期時會用到
self._months_map = {1:'Jan', 2:'Feb', 3:'Mar', 4:'Apr', 5:'May', 6:'Jun', 7:'Jul',
8:'Aug', 9:'Sep', 10:'Oct', 11:'Nov', 12:'Dec'}
def Run(self):#主函數,用于循環監聽用戶的登入請求
self.s.listen(1)
#self.s.settimeout(60)
print 'socket created server running...'
#設置之后創建的socket都只存活60秒,防止異常時卡死
socket.setdefaulttimeout(60)
while 1:
try:
conn,addr=self.s.accept()
#停止服務器
except KeyboardInterrupt:
self.close=True
conn.close()
self.s.close()
print 'KeyboardInterrupt'
break
print 'connect with '+addr[0]+':'+str(addr[1])
#開啟新線程用于與該用戶交互
start_new_thread(self.cftpcmd,(conn,))
def cftpcmd(self,cnn,):#與用戶交互的主函數
cpath=self.path.replace('\\','//')
os.chdir(cpath)
#連接文件化,以訪問文件的方式訪問socket
cf=cnn.makefile('rw',0)
cf.write('220 ready for transfer\r\n')
print 'thread open and connected...'
#無用戶驗證機制,在此接受用戶
print cf.readline().strip()
cf.write('331 name ok\r\n')
print cf.readline().strip()
cf.write('230 log in ok\r\n')
#保存用于數據傳輸的連接
dsocket=None
#用于處理用戶請求關閉連接
selfclose=False
while 1:
#獲取用戶提交的命令和參數
try:
gets=cf.readline().strip()
if self.close or selfclose:
break
except:
print '\r\ntimeout exit thread'
cnn.close()
break
print 'receive command: "%s"'% gets
cmd=gets[:3].lower()
args=gets[3:]
#解析命令,使用對應的函數處理。以eval方式是為了在多個命令需要的處理函數相似的情況下簡化
try:
if cmd in ['lis',]:
ev='self.handle_%s(dsocket,cf)' % (cmd)
print ev
eval(ev)
elif cmd=='qui':
selfclose=self.handle_qui(cf)
elif cmd=='ret':
cf.write('125 dataconnection open\r\n')
start_new_thread(self.handle_ret,(args,cf,dsocket))
elif cmd=='sto':
cf.write('150 file status ok\r\n')
start_new_thread(self.handle_sto,(args,cf,dsocket))
elif cmd=='pas':
ev='self.handle_%s("%s",cf)' % (cmd,args)
print ev
dsocket,psocket=eval(ev)
elif cmd=='rnf':
cf.write('350 ready for destination name\r\n')
oldename=args[2:]
elif cmd=='rnt':
cf.write('250 rename ok\r\n')
newname=args[2:]
try:
os.rename(oldename,newname)
except:
print 'rename error'
elif hasattr(self,'handle_%s'% cmd):
ev='self.handle_%s("%s",cf)' % (cmd,args)
print ev
eval(ev)
else:
cf.write('501 Invaild command\r\n')
print 'no handler for this command..'+'self.handle_%s("%s",cf)' % (cmd,args)
except:
print 'error...closing thread and conn'
if dsocket != None:
dsocket.close()
psocket.close()
cf.write('221 goodbye..\r\n')
cf.close()
cnn.close()
exit_thread()
print 'main thread exit'
cnn.close()
def handle_user(self,args,cf):
cf.write('331 username ok\r\n')
print '331 ok'
def handle_pass(self,args,cf):
cf.write('230 log in ok\r\n')
print '230 ok'
def handle_cwd(self,args,cf):#CWD函數,還包含了當目錄不存在時創建目錄的功能
try:
os.chdir(args[1:])
except:
print 'dir does not exit,make it'
os.mkdir(args[1:])
os.chdir(args[1:])
cf.write('250 "%s" is current directory\r\n'% os.getcwd())
print 'cwd'
def handle_pwd(self,args,cf):
cf.write('257 "%s" is current directory\r\n'% os.getcwd()[len(self.path)-1:].replace('\\','/'))
print 'pwd'
def handle_lis(self,ppsock,cf):#LIST函數,用于返回用戶請求的目錄下的文件列表
cf.write('125 Data connection already open \r\n')
res=''
for afile in os.listdir(os.getcwd()):
fpath=os.getcwd()+'\\'+afile
#文件的修改時間需要進行相應的格式化
tstr=self.format_time(fpath)
if os.path.isfile(fpath):
#獲取文件大小
size=os.path.getsize(fpath)
res+= '-rw-rw-rw- 1 owner group %s %s %s\r\n' % (size,tstr,afile)
else:
res+= 'drwxrwxrwx 1 owner group 0 %s %s\r\n' % (tstr,afile)
print res
ppsock.send(res)
cf.write('226 transfer complete\r\n')
ppsock.close()
def handle_pas(self,args,cf):#進入PASV模式,返回一個用于傳輸數據的socket
psock=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
psock.bind((self.lip,0))
pport=psock.getsockname()[1]
psock.listen(1)
cf.write('227 entering pasv mode (%s,%s,%s).\r\n' % (psock.getsockname()[0],pport//256,pport%256))
ppsock,addr=psock.accept()
print 'enter pasv mode port %s...'%pport
return [ppsock,psock]
def handle_typ(self,args,cf):
cf.write('200 \r\n')
print 'type a'
def handle_qui(self,cf):
cf.write('200 \r\n')
print 'quit...'
return True
def handle_noo(self,args,cf):
args=args[2:]
cf.write('200 \r\n')
print 'noop'
def handle_siz(self,args,cf):
filename=args[2:]
print filename
size=os.path.getsize(os.getcwd()+'\\'+filename)
cf.write('%s %s\r\n'%(213,size))
def handle_por(self,args,cf):#port mode pass
args=args[2:]
cf.write('200 \r\n')
print 'enter port mode'
def handle_ret(self,args,cf,psock):#RET命令,用于下載文件
try:
tpath=os.getcwd()+'\\'+args[2:]
print 'ret transfering now...path:%s'%tpath
f=open(tpath,'rb')
#對文件進行分片傳輸
while True:
data=f.read(self.PSIZE)
if not data:
break
psock.send(data)
cf.write('226 ok\r\n')
print 'transport completed..'
psock.close()
except:
print 'ret error...'
cf.write('226 ok\r\n')
psock.close()
exit_thread()
def handle_sto(self,args,cf,psock):#STO命令,用于上傳文件
try:
fname=os.getcwd()+'\\'+args[2:]
f=open(fname,'wb')
print 'make file ok'
buf=psock.recv(self.PSIZE)
while len(buf)==self.PSIZE:
f.write(buf)
buf=psock.recv(self.PSIZE)
cf.write('226 transfer complete\r\n')
f.write(buf)
f.close()
psock.close()
except:
print 'error in sto'
psock.close()
exit_thread()
def handle_mkd(self,args,cf):
cf.write('257 %s dir created\r\n'%args)
try:
os.mkdir(args[1:])
except:
print 'mkdir error'
def handle_del(self,args,cf):
cf.write('250 file removed\r\n')
fname=os.getcwd()+'\\'+args[2:]
try:
os.remove(fname)
except:
print 'dele error'
def handle_rmd(self,args,cf):
cf.write('250 dir remove\r\n')
try:
os.rmdir(args[1:])
except:
print 'remove dir error'
def format_time(self,file):#時間格式化
raw_ftime=os.stat(file).st_mtime
mtime=time.localtime(raw_ftime)
now=time.time()
if now-raw_ftime>180*24*60*60:
tstr='%d %Y'
else:
tstr='%d %H:%M'
res='%s %s'%(self._months_map[mtime.tm_mon],time.strftime(tstr,mtime))
return res
def handle_sys(self,args,cf):
cf.write('215 UNIX Type:L8\r\n')
print 'syst'
if name=='main':
abc=FTPs('192.168.8.100','e:/')
abc.Run()</pre>
來自:http://blog.csdn.net/whu_ksp/article/details/8562477
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!
|