Python自動化開發從淺入深-進階(socketServer)

socketserver:
   --- 每個鏈接過來都會單首創建一個線程。
   ---socketserver處理的線程數目與服務器資源配置有關
socketserver有四個基本的類:
  --- TCPServer
  ---UDPServer
  ---UnixStreamServer
  ---UnixDatagramServer
這四個類的同步請求,每個請求只有完成後才能進行下一個請求。不適合進行長時間的大數據計算,因此須要單首創建進程或線程來處理每一個請求。ForkingMixIn和ThreadingMixIn一般被用於支持異步的行爲。
5個類的繼承關係以下圖:

 

咱們經常使用的類是:python

ThreadingTCPServerlinux

ThreadingUDPServershell

ForkingTCPServerwindows

ForkingUDPServer服務器

一個簡單例子:
import socketserver

class MyTCHHandler(socketserver.BaseRequestHandler):

    #當socketserver建立實例並運行後,便在這裏進行通訊處理
    def handle(self):
        pass


if __name__ == '__main__':
    HOST,PORT = 'localhost',5007

#建立socketserver實例,將參數及MyTCPHandler傳進去
server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler)
#啓動server,此時server將一直運行
server.serve_forever()

 完整實例:ssh

#!/usr/bin/env python
#__author__:ZhaoHong
# -*- coding: utf-8 -*-

import socketserver

import time,os,sys,re,hashlib
from subprocess import PIPE,Popen
from multiSocketFTP.multiFTPServer.set import setting

_FILE_SLIM = (100*1024*1024) # 100MB

class FTPServerHandle(socketserver.BaseRequestHandler):
    '''
      It's a class for handle FTPServer.
      socketserver is a multthreading base class,
    '''
    def handle(self):

        while True:
            ########## -------------------------------------------------------------------------------------------------beginning recive
            self.data = self.receiveFromClient(1024)  # ----------------------------------------------------------------receive
            print ("from client : [{}] \nthe server has got data is : [{}]" \
                   .format(self.client_address[0],self.data))#print gotted message
            ########## -------------------------------------------------------------------------------------------------have not data
            if not self.data:#if data is not sented,we will break loop.
                print ("client {} stop to send data.".format(self.client_address[0]))
                break
            ########## -------------------------------------------------------------------------------------------------receive data handle here
            ## cmdHead : judge "handle branch"
            ##################################
            #we will handle the data of recieve,follow the agreement below:
            user_input = self.data.split()                #use split fuction to make a list for data
            cmdHead = user_input[0]                       #get head about input command from client

            if cmdHead == 'welcome':                    #if head is welcome then show welcome message. it's show at begining
                print("welcome to use FTP SERVER...")
                self.sendToClient('Welcome to use FTP\n=================') ## ------------------------------------welcome  <send > 1

            #self.request.sendall(bytes('Welcome to use FTP\n=================','utf-8'))
            if cmdHead == 'welcome_show_ok':
                continue
            #---------------------------------------------------------------------------------------------------------- login --
            #fisrt we must handle login process,otherwish we dont handle other command from client.
            # (user_input list length must be 3)
            elif cmdHead == 'login' and len(user_input) == 3:
                cmdUserName = user_input[1]                   #get user name from client
                cmdUserPassword = user_input[2]               #get user password from client
                #****** admin login begin *********
                (isLogin,username,directory,quota) = self.userlogin(cmdUserName,cmdUserPassword,setting.userInfo)##----- Login Infomation---
                if isLogin:##------------------------------------------------------------------------------------------ enter Login ###
                    print("{}:login FTP success!".format(cmdUserName))
                    self.sendToClient('loginSuccess')##-------------------------------------------------------------- login sucess  <send> 2
                    if setting.sysStr == 'Windows':
                        os.chdir("%s\\%s"%(setting.homeDIR,cmdUserName))     # cd to acount home drectory(windows)
                        os.system("cd")
                    else:
                        os.chdir("%s/%s"%(setting.homeDIR,cmdUserName))      # cd to acount home drectory(linux)
                        os.system("pwd")
                    while True:#--------------------------------------------------------------------------------------{loop begin}  FTP cmd handle
                        #命令格式:cd 目錄
                        #          get filename
                        #          put filename
                        ret = self.receiveFromClient(1024) #------------------------------------------------------------wait   [recv] 1
                        ftpInput = ret.split()    #split string to list (separator is spaces)
                        if ret == '':continue
                        print("command:%s"%ret)
                        if re.search(r"^cd",ret) and len(ftpInput) >= 2:#----------------------------------------------cd DIR
                            print(user_input[1])
                            if setting.sysStr == "Windows":
                                if ftpInput[1] == 'home':
                                    Directory = "%s%s"%(setting.homeDIR,username)
                                else:
                                    Directory = "%s%s\\%s"%(setting.homeDIR,username,ftpInput[1])
                            else:
                                if ftpInput[1] == 'home':
                                    Directory = "%s%s"%(setting.homeDIR,username)
                                else:
                                    Directory = "%s%s/%s"%(setting.homeDIR,username,ftpInput[1])
                            #cmd = "cd %s"%Directory
                            try:
                                os.chdir(Directory)
                                self.sendToClient(Directory)
                            except:
                                self.sendToClient("directory is not found")
                                print("directory is not found")
                        #-----------------------------------------------------------------------------------------------ls,dir
                        elif (re.search(r"^ls",ret) and len(ftpInput) >= 1) or \
                              re.search(r"^dir",ret) :
                            retDIR = os.popen(ret).read()
                            self.sendToClient(retDIR)
                            #if setting.sysStr == "Windows":
                                #self.sendToClient(bytes(retDIR,'gbk'))
                            #else:
                            #    self.sendToClient(bytes(retDIR,'gbk'))
                        #------------------------------------------------------------------------------------------------get srcFilename drcFilename srcDIR drcDIR
                        elif re.search(r"^get",ret):#開始 get
                            print ("starting to get file ...")
                            if setting.sysStr == "Windows":#獲取文件名
                                pathFile = "%s%s\\%s"%(setting.homeDIR,ftpInput[3],ftpInput[1])
                                saveSizeFile = "%s%s\\%s"%(setting.homeDIR,ftpInput[3],"srcFileSize.dat")
                                saveBreakFile = "%s%s\\%s"%(setting.homeDIR,ftpInput[3],"breakAt.dat")
                            else:
                                pathFile = "%s%s/%s"%(setting.homeDIR,ftpInput[3],ftpInput[1])#ftpInput[1]: path,ftpInput[2]:src filename ftpInput[2]: drc filename
                                saveSizeFile = "%s%s\\%s"%(setting.homeDIR,ftpInput[3],"srcFileSize.dat")
                                saveBreakFile = "%s%s\\%s"%(setting.homeDIR,ftpInput[3],"breakAt.dat")
                            self.request.sendall(b"get")#---------------------------------------------------------------send get
                            msg = self.request.recv(500) #---------------------------------------------------------------recv (獲取文件客戶端是否已經下載這個文件)
                            msg = str(msg,'utf-8')
                            msg1 = msg.split('|')
                            if msg1[0] == 'notHaveFile':#若是沒有下載,就從頭開始下載
                                md5Value = self.file_md5(pathFile)#獲取源文件的MD5值
                                fileSize = self.writeFileSize(pathFile,saveSizeFile)#獲取源文件的總計數
                                self.getFile(pathFile,saveBreakFile,fileSize,md5Value)  #----------------------------------------------------------function getfile
                            elif msg1[0] == 'MD5':#不然,開始斷點續傳
                                fromClientFileMD5 = msg1[1]#獲取客戶端的MD5值
                                md5Value = self.file_md5(pathFile)#獲取源文件的MD5值

                                fileSize = self.writeFileSize(pathFile,saveSizeFile)#獲取源文件的總計數
                                if md5Value == fromClientFileMD5:#對比服務端源文件和客戶端目標文件的MD5值,相等
                                    print("文件已經成功下載")
                                    self.request.sendall(b'fileIsdownload')#------------------------------------------send   文件已經下載 fileIsdownload
                                else:#不然,進入斷點續傳
                                    breakat = self.getBreakAT(pathFile)#獲取斷點位置數字
                                    self.getBreakFile(pathFile,saveBreakFile,breakat,fileSize,md5Value)#-----------------進入斷點續傳 function

                        #-----------------------------------------------------------------------------------------------put srcFilename drcFilename srcDIR drcDIR
                        elif re.search(r"^put",ret):
                            if setting.sysStr == "Windows":
                                fileName = "{}{}\\{}".format(setting.homeDIR,ftpInput[4],ftpInput[2])
                            else:
                                fileName = "{}{}/{}".format(setting.homeDIR,ftpInput[4],ftpInput[2])
                                #fileName = "{}/{}".format(setting.homeDIR,head[1])
                            print(fileName)

                            t = os.path.isfile(fileName)
                            if t:
                                print(fileName)
                                fileMD5 = self.file_md5(fileName)
                                s = "MD5|{}".format(fileMD5)
                                self.request.recv(200)#--------------------------------------------------------------------------------recv b'get'
                                self.request.sendall(bytes(s,'utf-8'))#---------------------------------------------------------------send MD5
                                lineSize = 0
                                self.putFile(self.request,fileName)
                            else:
                                self.request.recv(200)#--------------------------------------------------------------------------------recv b'get'---(1)
                                self.request.sendall(b'notHaveFile|a')#--------------------------------------------------------send (notHaveFile)
                                self.putFile(self.request,fileName)
                        elif re.search(r"^quitFTP",ret):#--------------------------------------------------------------quit
                                self.request.sendall(b'OK')
                        else:#------------------------------------------------------------------------------------------other handle
                            retForCmd = Popen(ret,shell=True,stdout=PIPE).stdout.read()#

                            self.request.sendall(retForCmd)

                            print(retForCmd)
                else:
                    self.sendToClient('<login failure>')
            elif cmdHead == "ssh":
                self.sshHandle()
            else:
                pass

    def receiveFromClient(self,length):
        self.data = self.request.recv(length).strip()#waiting for receiv message,it's can get 1024 character one time
        self.data = str(self.data,'utf-8')

        return self.data

    def sendToClient(self,str):
        self.request.sendall(bytes(str,'utf-8'))

    def sshHandle(self):
        while True:
            self.sendToClient("\nssh ready to receive..")
            #time.sleep(1)
            cmd = self.receiveFromClient(1024)
            if len(cmd) == 0:continue
            if cmd == 'q':
                print("quit ssh...")
                break
            retForCmd = Popen(cmd,shell=True,stdout=PIPE).stdout.read()

            #retForCmd = self.PopenHandle(cmd)
            #print("cmd ret:{}".format(retForCmd))
            if len(retForCmd) == 0:
                retForCmd = b"no data to return!"
            len1 = str(len(retForCmd))
            lenghSend ="lineSize:%s"%(len1)

            self.sendToClient(lenghSend)
            time.sleep(1)
            print("send data size is : {}".format(lenghSend))
            clientRet = self.receiveFromClient(100)
            if clientRet == "readyToGo":
                #retForCmd = "%s"%retForCmd
                #retForCmd = retForCmd.decode()
                self.request.sendall(retForCmd)
                #.requestsendall(retForCmd)
                print(retForCmd)

    def PopenHandle(self,str):
        retResult = Popen(str,shell=True,stdout=PIPE).stdout.read()
        return retResult

    def userlogin(self,username,password,msgDict):
        '''
        用戶登錄
        :param username:帳戶名
        :param password: 口令
        :param msgDict: 帳戶字典 {username:[口令,磁盤目錄名,磁盤配額(MB)]}
        :return: isLogin,username,directory,quota
        '''
        #print(AdminMsgList[0][0],AdminMsgList[1][0],AdminMsgList)
        isLogin = False
        for k,v in msgDict.items():
            if k == username and msgDict[k][0] == password:
                isLogin = True
                directory = msgDict[k][1]
                quota = msgDict[k][2]
                return isLogin,username,directory,quota
        else:
            return isLogin,'','',''

    def cmd(self,cmdStr):
        '''
        接收一個ssh命令
        :param cmdStr: 命令
        :return: 命令以後的結果
        '''
        ret = Popen(cmdStr,shell=True,stdout =PIPE).stdout.read()
        print(ret)
        return ret

    def putFile(self,obj,fileName):
        '''
        寫文件:從客戶端傳來的文件
        :return:
        '''
        srcfileMD5 = ''
        drcfileMD5 = ''
        while True:
            f = open(fileName,'ab')#--------------------------以累加的形式ab設置文件句柄
            #print("1")
            long = obj.recv(100)#-------------------------------------------------------------------------recv 所傳的長度,用於寫入
            print(long)
            slong = str(long,'utf8')
            if slong == 'alldone':#---------------------------------傳輸完畢的處理
                drcfileMD5 = self.file_md5(fileName)#上傳完成的文件MD5值
                if srcfileMD5 == drcfileMD5:#與客戶端文件的MD5比較
                    print("file download is done !")#相等,則下載成功
                else:
                    print('file download is failure !')#不等,下載失敗
                break
            ilong = int(slong)
            obj.sendall(b'ok')#------------------------------------------------------------------------send 迴應所傳來的信息長度
            data = obj.recv(ilong) #--------------------------------------------------------------------recv 以傳來的長度設置接收長度,開始接收數據
            f.write(data)#寫傳來的數據到文件
            f.close()#關閉文件,之因此在循環裏面打開,關閉文件是爲了保證每次數據都能存下,便於之後斷點續傳。
            obj.sendall(b"getline")#------------------------------------------------------------------send 迴應獲取了一行數據
            #data1 = str(data,'utf-8')
            msg = obj.recv(500)#-----------------------------------------------------------------------recv  接收一個信息,包含:所傳文件的總大小,斷點位置和MD5值
            #print (slong,msg)
            msg1= str(msg,'utf8')

            isMsg = msg1.split('|')
            #print(type(data))
            if isMsg[0] == 'msg':#獲取所傳文件的總大小,斷點位置和MD5值
                filesize = int(isMsg[1])
                fileBreak = int(isMsg[2])
                srcfileMD5 = isMsg[3]
                self.progressBar(fileBreak,filesize,"finish :")#---------------------進度條
            #lineSize += len(data)

            obj.sendall(b"getMsg")#-----------------------------------------------------------------send 迴應接收到msg
            #---------------------------------------------------------------------------------------------------------------繼續循環
            #done = obj.recv(100)

    def getFile(self,srcFileName,sizeBreakpointFile,srcFileSizeCount,MD5):
        '''
        讀文件:獲取一個須要下載的文件  get file to download
        :param srcFileName:準備要下載的源文件:
        :param sizeBreakpointFile:存放斷點位置的文件:
        :param srcFileSizeCount:源文件的readline總計數
        :param:MD5:源文件的MD5值
        :return:
        '''
        #print(srcFileName)
        breakpointAt = 0  #初始化斷點位置
        #開始傳輸文件
        t = os.path.isfile(sizeBreakpointFile)#若是存放斷點的文件存在,先把他刪除,以便從下一個斷點往下記
        if t:
            os.remove(sizeBreakpointFile)
        isFile = os.path.isfile(srcFileName)#查看須要下載的文件是否存在

        #若是須要下載的源文件存在,就開始get的動做
        if isFile:
            srcRf = open(srcFileName,'rb')#獲取源文件句柄
            while breakpointAt < srcFileSizeCount:#不斷記錄斷點位置,並對比源文件總計數。以防止出現異常中斷
                #yield
                line = srcRf.readline() #---------------源文件讀一行
                l = len(line)
                sl = str(l)
                self.request.sendall(bytes(sl,'utf8'))#----------------------------------------------------------------send 發送一行信息的長度
                for i in range(200):
                    pass
                #print(sl)
                self.request.recv(100)#---------------------------------------------------------------------------------recv 等待迴應,不作處理
                #time.sleep(0.1)
                self.request.sendall(line)# 發給客戶端一行--------------------------------------------------------------send 發送一行信息
                self.request.recv(200)#接收一個 get返回信息-------------------------------------------------------------recv 等待迴應,不作處理
                breakAtf = open(sizeBreakpointFile,'a')#打開記錄斷點的文件句柄
                breakpointAt += 1#斷點計數
                s = str(breakpointAt)
                s = "%s\n"%s
                breakAtf.write(s)#寫斷點位置計數--------------------------寫一個斷點
                breakAtf.close()#關閉句柄

                msg = "msg|{}|{}|{}".format(srcFileSizeCount,breakpointAt,MD5)#----------msg|源文件總數|斷點位置|MD5
                self.request.sendall(bytes(msg,'utf8'))#----------------------------------------------------------------send  發送 msg|源文件總數|斷點位置|MD5
                self.request.recv(200)#----------------------------------------------------------------------------------rece  等待迴應,不作處理
                #word = " [正在傳輸 %s]"%srcFileName
                self.progressBar(s,srcFileSizeCount,"finish:") #--------------------進度條
            else:
                srcRf.close()#-------------------------------------------------------------------------------------------傳輸完成
                self.request.sendall(bytes('alldone','utf8'))#---------------------------------------------------------send ‘alldone’傳輸完成標誌

    def getBreakFile(self,srcFileName,sizeBreakpointFile,breakpointAt,srcFileSizeCount,MD5):
        '''
        源文件斷點續傳
        :param srcFileName:
        :param sizeBreakpointFile:
        :param breakAt:
        :param countAll:
        :return:
        '''
        t = os.path.isfile(sizeBreakpointFile)#若是存放斷點的文件存在,先把他刪除,以便從下一個斷點往下記
        if t:
            os.remove(sizeBreakpointFile)
        isFile = os.path.isfile(srcFileName)#查看須要下載的文件是否存在
        i = 0
        #若是須要下載的源文件存在,就開始get的動做
        if isFile:
            srcRf = open(srcFileName,'rb')#獲取源文件句柄
            while i < srcFileSizeCount:#不斷記錄斷點位置,並對比源文件總計數。以防止出現異常中斷
                i += 1#斷點計數
                if i > breakpointAt:#進入斷點續傳條件
                    line = srcRf.readline() #源文件讀一行
                    self.request.sendall(line)# 發給客戶端一行---------------------------------------------------------------send
                    self.request.recv(200)#接收一個 get返回信息--------------------------------------------------------------recv
                    breakAtf = open(sizeBreakpointFile,'a')#打開記錄斷點的文件句柄
                    #drcWf = open(drcFileName,'ab')
                    #drcWf.write(line)#寫目標文件
                    s = str(breakpointAt)
                    s = "%s\n"%s
                    #print(breakpointAt)
                    breakAtf.write(s)#寫斷點位置計數
                    breakAtf.close()
                    #drcWf.close()
                    msg = "msg|{}|{}".format(srcFileSizeCount,breakpointAt)#發送 msg|源文件總數|斷點位置
                    self.request.sendall(bytes(msg,'utf8'))#----------------------------------------------------------------send
                    self.request.recv(200)#----------------------------------------------------------------------------------rece   get
                    word = " [正在傳輸 %s]"%srcFileName
                    self.progressBar(s,srcFileSizeCount,word) #進度條
            srcRf.close()#-----------------------------------------------------------------------------------------------傳輸完成
            lastMsg = "alldone|{}".format(MD5)#------------------------------------------------------------------------send     最後發送源文件的MD5值
            self.request.sendall(bytes(lastMsg,'utf8'))


    def file_md5(self,filename):
         #calltimes = 0
         hmd5 = hashlib.md5()
         fp = open(filename,"rb")
         f_size = os.stat(filename).st_size
         if f_size>_FILE_SLIM:
             while(f_size>_FILE_SLIM):
                 hmd5.update(fp.read(_FILE_SLIM))
                 f_size/=_FILE_SLIM
                 #calltimes += 1   #delete
             if(f_size>0) and (f_size<=_FILE_SLIM):
                 hmd5.update(fp.read())
         else:
             hmd5.update(fp.read())

         return hmd5.hexdigest()

    def writeFileSize(self,fileName,sizeFile):
        sizeWf = open(sizeFile,'w+')#文件總大小文件開啓   ,準備讀
        count = 0
        with open(fileName,'rb') as srcRf:#循環以獲取文件總數
            for line in srcRf:
                count += 1
        s = str(count)#存儲總數到文件 1.txt
        s = "%s\n"%s
        sizeWf.write(s)
        sizeWf.close()

        return count

    def getBreakAT(self,fileName):
        i = 0
        with open(fileName,'r') as srcRf:#循環以獲取文件總數
            for line in srcRf:
                lastRead = line
            #print(type(lastRead))

            i = int(lastRead.strip())


        return i

    def progressBar(self,num=1, sum=100,bar_word=":"):
        rate = float(num) / float(sum)
        rate_num = int(rate * 100)
        temp = '\r%d %% %s' % (rate_num,bar_word)
        sys.stdout.write(temp)
        sys.stdout.flush()
##################################
class clsSocketServer(object):

    def __init__(self,host,port):
        self.host = host
        self.port = port

    def startServer(self,obj):
        server = socketserver.ThreadingTCPServer((self.host,self.port),obj)
        server.serve_forever()
完整實例server端
#!/usr/bin/env python
#__author__:ZhaoHong
# -*- coding: utf-8 -*-

import socket,time,os,sys,hashlib
from multiSocketFTP.multiFTPServer.set import setting
#from socket_FTPClient.bin import main

_FILE_SLIM = (100*1024*1024) # 100MB
##################################
class clsSocketClient(object):

    def __init__(self,host,port):
        '''
        init to get :host,port
        :param host:
        :param port:
        :return:
        '''
        self.host = host
        self.port = port

    def myConnect(self):
        '''
        鏈接到服務器 conn to server
        :return:
        '''
        s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s.connect((self.host,self.port))
        return s

    def receiveFromServer(self,obj,length):
        '''
        接收信息 receive from server
        :param obj:
        :param length:
        :return:
        '''
        data = obj.recv(length).strip()#waiting for receiv message,it's can get 1024 character one time
        if isinstance(data,bytes):

            try:
                data = str(data,'gbk')
            except:
                data = str(data,'utf-8')

        return data

    def sendToServer(self,obj,str):
        '''
        send message to server
        :param obj:
        :param str:
        :return:
        '''
        obj.sendall(bytes(str,'utf-8'))

    def login(self,objSocket):
        '''
        登陸處理 user login handle
        :param objSocket:
        :return:
        '''
        cmdLogin = input("input<<< login user password>>> ").strip()#Login command input
        loginCmd = cmdLogin.split()
        if loginCmd[0] == 'q':return 'quit',''
        if loginCmd[0]=='login' and len(loginCmd) == 3:#judge : is "login" string and two blank space

            self.sendToServer(objSocket,"{} {} {}".format(loginCmd[0],loginCmd[1],loginCmd[2]))
            loginISVerification = self.receiveFromServer(objSocket,1024)

            return loginISVerification,loginCmd[1]

    def cmdHandle(self,objSocket,user):
        '''
        ssh 命令處理  command handle here
        :param objSocket:
        :param user:
        :return:
        '''
        print("{} login success!".format(user))
        while True:#begin to waiting input FTP command
            cmdGet = input("FTP command here>>> ").strip()#FTP command input
            if len(cmdGet) == 0 : continue #if FTP command is blank then continue
            head = cmdGet.split()

            if (head[0] == "cd" and len(head) == 2) or \
               (head[0] == "ls" and len(head) >= 1)  or \
                    (head[0] == "dir" and len(head) >= 1):
                self.sendToServer(objSocket,cmdGet)#--------------------------------------------------------------------send FTP cmd
                time.sleep(1)
                ret = self.receiveFromServer(objSocket,1024)#-----------------------------------------------------------rev
                print(ret)
                #self.sendToServer(objSocket,"OK")

            elif (head[0] == "get" and len(head) >= 2):#--------------------------------------------------------------get
                print("start downloading")
                self.sendToServer(objSocket,cmdGet)#--------------------------------------------------------------------send get cmd 發送一個命令給服務端
                if setting.sysStr == "Windows":
                    fileName = "{}{}\\{}".format(setting.homeDIR,head[4],head[2])
                else:
                    fileName = "{}{}/{}".format(setting.homeDIR,head[4],head[2])
                    #fileName = "{}/{}".format(setting.homeDIR,head[1])
                t = os.path.isfile(fileName)
                if t:
                    print(fileName)
                    fileMD5 = self.file_md5(fileName)
                    s = "MD5|{}".format(fileMD5)
                    objSocket.recv(200)#--------------------------------------------------------------------------------recv b'get'
                    objSocket.sendall(bytes(s,'utf-8'))#---------------------------------------------------------------send MD5
                    lineSize = 0
                    self.writeToFile(objSocket,fileName)
                else:
                    objSocket.recv(200)#--------------------------------------------------------------------------------recv b'get'---(1)
                    objSocket.sendall(b'notHaveFile|a')#--------------------------------------------------------send (notHaveFile)
                    self.writeToFile(objSocket,fileName)
##########################################################################################################################
            elif (head[0] == "put" and len(head) >= 2):
                print("start upload")
                self.sendToServer(objSocket,cmdGet)
                if setting.sysStr == "Windows":
                    pathFile = "%s%s\\%s"%(setting.homeDIR,head[3],head[1])
                    saveSizeFile = "%s%s\\%s"%(setting.homeDIR,head[3],"srcFileSize.dat")
                    saveBreakFile = "%s%s\\%s"%(setting.homeDIR,head[3],"breakAt.dat")
                else:
                    pathFile = "%s%s/%s"%(setting.homeDIR,head[3],head[1])#ftpInput[1]: path,ftpInput[2]:src filename ftpInput[2]: drc filename
                    saveSizeFile = "%s%s\\%s"%(setting.homeDIR,head[3],"srcFileSize.dat")
                    saveBreakFile = "%s%s\\%s"%(setting.homeDIR,head[3],"breakAt.dat")

                objSocket.sendall(b"get")#---------------------------------------------------------------send get
                msg = objSocket.recv(500) #---------------------------------------------------------------recv (獲取文件客戶端是否已經下載這個文件)
                msg = str(msg,'utf-8')
                msg1 = msg.split('|')
                if msg1[0] == 'notHaveFile':#若是沒有下載,就從頭開始下載
                    md5Value = self.file_md5(pathFile)#獲取源文件的MD5值
                    fileSize = self.writeFileSize(pathFile,saveSizeFile)#獲取源文件的總計數
                    self.putFile(objSocket,pathFile,saveBreakFile,fileSize,md5Value)  #----------------------------------------------------------function getfile
                elif msg1[0] == 'MD5':#不然,開始斷點續傳
                    fromClientFileMD5 = msg1[1]#獲取客戶端的MD5值
                    md5Value = self.file_md5(pathFile)#獲取源文件的MD5值

                    fileSize = self.writeFileSize(pathFile,saveSizeFile)#獲取源文件的總計數
                    if md5Value == fromClientFileMD5:#對比服務端源文件和客戶端目標文件的MD5值,相等
                        print("文件已經成功下載")
                        objSocket.sendall(b'fileIsdownload')#------------------------------------------send   文件已經下載 fileIsdownload
                    else:#不然,進入斷點續傳
                        breakat = self.getBreakAT(pathFile)#獲取斷點位置數字
                        self.putBreakFile(objSocket,pathFile,saveBreakFile,breakat,fileSize,md5Value)#-----------------進入斷點續傳 function

            elif (head[0] == "ssh" and len(head) >= 1):
                cmd = ''
                for i in range(len(head)):
                    if i>0:
                        cmd += head[i]
                        cmd = cmd.strip()
                        print(cmd)
                self.sendToServer(objSocket,cmd)
                time.sleep(1)
                ret = self.receiveFromServer(objSocket,1024)

                print(ret)
            elif head[0] == "q":
                self.sendToServer(objSocket,head[0])
                break
            else:
                self.sendToServer(objSocket,cmdGet)
                ret = self.receiveFromServer(objSocket,1024)
                #ret = ret[2:-1]
                print(ret)

        return True

    def ssh(self,objSocket):
        '''
        ssh command>>
        :param objSocket:
        :return:
        '''
        self.sendToServer(objSocket,"ssh")
        #s.sendall(bytes("ssh",'utf-8'))
        time.sleep(1)
        sshReady =self.receiveFromServer(objSocket,100)
        #sshReady = str(s.recv(100),'utf-8')
        print(sshReady)
        time.sleep(1)
        size = 0
        while True:
            cmdSSH = input("command >>> ").strip()#------------------------------------------------------輸入ssh命令
            if len(cmdSSH) == 0:continue
            self.sendToServer(objSocket,cmdSSH)#-----------------------------------------------------------send 發出一個ssh命令
            retSize = self.receiveFromServer(objSocket,100)#-----------------------------------------------recv 接收大小標誌
            sizeMsg = str(retSize).split(":")
            if sizeMsg[0] == "lineSize":#----------------
                print("send ready to go")
                size = sizeMsg[1]#----獲取大小
                time.sleep(1)
                self.sendToServer(objSocket,"readyToGo")#----------------------------------------------send 準備好信息
                #s.sendall(bytes("readyToGo",'utf-8'))
                time.sleep(1)
            res =b''
            recv_size = 0

            while recv_size < int(size):#------------------------循環接收信息,直到收取完畢
                sshReturn = objSocket.recv(1024)#-------------------------------------------------------recv 接收返回結果信息

                recv_size += len(sshReturn)
                res += sshReturn
            else:#-----------------------------------------------------------------------------------最後打印返回結果
                if setting.sysStr=='Windows':
                    res = str(res,'gbk')
                else:
                    res = str(res,'utf8')
                print(res)
            if cmdSSH == "q":
                self.sendToServer(objSocket,"quit")#------------------------------send 退出信息
                break

    def putFile(self,obj,srcFileName,sizeBreakpointFile,srcFileSizeCount,MD5):
        '''
        get file to download
        :param srcFileName:準備要下載的源文件:
        :param sizeBreakpointFile:存放斷點位置的文件:
        :param srcFileSizeCount:源文件的readline總計數
        :param:MD5:源文件的MD5值
        :return:
        '''
        #print(srcFileName)
        breakpointAt = 0  #初始化斷點位置
        #開始傳輸文件
        t = os.path.isfile(sizeBreakpointFile)#若是存放斷點的文件存在,先把他刪除,以便從下一個斷點往下記
        if t:
            os.remove(sizeBreakpointFile)
        isFile = os.path.isfile(srcFileName)#查看須要下載的文件是否存在

        #若是須要下載的源文件存在,就開始get的動做
        if isFile:
            srcRf = open(srcFileName,'rb')#獲取源文件句柄
            while breakpointAt < srcFileSizeCount:#不斷記錄斷點位置,並對比源文件總計數。以防止出現異常中斷
                #yield
                line = srcRf.readline() #源文件讀一行
                l = len(line)
                sl = str(l)
                obj.sendall(bytes(sl,'utf8'))
                obj.recv(100)
                for i in range(200):
                    pass
                #print(sl)
                obj.sendall(line)# 發給客戶端一行---------------------------------------------------------------send
                obj.recv(200)#接收一個 get返回信息--------------------------------------------------------------recv
                breakAtf = open(sizeBreakpointFile,'a')#打開記錄斷點的文件句柄

                breakpointAt += 1#斷點計數
                s = str(breakpointAt)
                s = "%s\n"%s

                breakAtf.write(s)#寫斷點位置計數
                breakAtf.close()

                msg = "msg|{}|{}|{}".format(srcFileSizeCount,breakpointAt,MD5)#---------------------------------------------------發送 msg|源文件總數|斷點位置
                obj.sendall(bytes(msg,'utf8'))#----------------------------------------------------------------send
                obj.recv(200)#----------------------------------------------------------------------------------rece   get
                #word = " [正在傳輸 %s]"%srcFileName
                self.progressBar(s,srcFileSizeCount,"finish") #進度條
            else:
                srcRf.close()#-----------------------------------------------------------------------------------------------傳輸完成
                #lastMsg = "alldone|{}".format(MD5)#------------------------------------------------------------------------send     最後發送源文件的MD5值
                obj.sendall(bytes('alldone','utf8'))

    def writeToFile(self,obj,fileName):
        '''
        寫入:對get命令的寫操做
        :param obj: socket對象
        :param fileName: 寫入的文件名
        :return:
        '''
        srcfileMD5 = ''
        drcfileMD5 = ''
        while True:
            f = open(fileName,'ab')#-----------------以ab方式打開文件句柄
            #print("1")
            long = obj.recv(100)#---------------------------------------------------------------------------------------recv 接收一個傳來的文件信息長度
            slong = str(long,'utf8')
            if slong == 'alldone':#----------------------傳輸結束處理
                drcfileMD5 = self.file_md5(fileName)
                if srcfileMD5 == drcfileMD5:
                    print("file download is done !")
                else:
                    print('file download is failure !')
                break
            ilong = int(slong)
            obj.sendall(b'ok')    #------------------------------------------------------------------------------------send 迴應對端,不作處理
            data = obj.recv(ilong) #------------------------------------------------------------------------------------recv 開始接收數據
            f.write(data)#----------------------------------------寫收到的信息行
            f.close()#--------------------------------------------關閉句柄
            obj.sendall(b"getline")#----------------------------------------------------------------------------------send 迴應對端,不作處理
            #data1 = str(data,'utf-8')
            msg = obj.recv(500) #---------------------------------------------------------------------------------------recv  msg|源文件總數|斷點位置|MD5
            msg1= str(msg,'utf8')

            isMsg = msg1.split('|')
            #print(type(data))
            if isMsg[0] == 'msg':#處理爲 msg|源文件總數|斷點位置|MD5
                filesize = int(isMsg[1])
                fileBreak = int(isMsg[2])
                srcfileMD5 = isMsg[3]
                self.progressBar(fileBreak,filesize,"finish :")#---------------------進度條
            #lineSize += len(data)

            obj.sendall(b"getMsg")#-----------------------------------------------------------------------------------send 迴應對端,不作處理
            #--------------------------------------------------------------------------------------循環

    def progressBar(slef,num=1, sum=100,bar_word=":"):
        '''
        處理進度條
        :param num:
        :param sum:
        :param bar_word:
        :return:
        '''
        rate = float(num) / float(sum)
        rate_num = int(rate * 100)
        temp = '\r%d %% %s' % (rate_num,bar_word)
        sys.stdout.write(temp)
        sys.stdout.flush()

    def file_md5(self,filename):
         '''
          生成文件的MD5值
         :param filename:
         :return:
         '''

         #calltimes = 0
         hmd5 = hashlib.md5()
         fp = open(filename,"rb")
         f_size = os.stat(filename).st_size
         if f_size>_FILE_SLIM:
             while(f_size>_FILE_SLIM):
                 hmd5.update(fp.read(_FILE_SLIM))
                 f_size/=_FILE_SLIM
                 #calltimes += 1   #delete
             if(f_size>0) and (f_size<=_FILE_SLIM):
                 hmd5.update(fp.read())
         else:
             hmd5.update(fp.read())

         return hmd5.hexdigest()

    def writeFileSize(self,fileName,sizeFile):
        '''
        計算文件的總大小(總計數)
        :param fileName:
        :param sizeFile:
        :return:
        '''
        sizeWf = open(sizeFile,'w+')#文件總大小文件開啓   ,準備讀
        count = 0
        with open(fileName,'rb') as srcRf:#循環以獲取文件總數
            for line in srcRf:
                count += 1
        s = str(count)#存儲總數到文件 1.txt
        s = "%s\n"%s
        sizeWf.write(s)
        sizeWf.close()

        return count

    def getBreakAT(self,fileName):
        '''
        獲取文件斷點位置
        :param fileName:
        :return:
        '''
        i = 0
        lastRead = '1'
        with open(fileName,'r') as srcRf:#循環以獲取文件總數
            for line in srcRf:
                lastRead = line
            #print(type(lastRead))

            i = int(lastRead.strip())


        return i

    def putBreakFile(self,obj,srcFileName,sizeBreakpointFile,breakpointAt,srcFileSizeCount,MD5):
        '''
        寫文件:源文件斷點續傳
        :param srcFileName:
        :param sizeBreakpointFile:
        :param breakAt:
        :param countAll:
        :return:
        '''
        t = os.path.isfile(sizeBreakpointFile)#若是存放斷點的文件存在,先把他刪除,以便從下一個斷點往下記
        if t:
            os.remove(sizeBreakpointFile)
        isFile = os.path.isfile(srcFileName)#查看須要下載的文件是否存在
        i = 0
        #若是須要下載的源文件存在,就開始get的動做
        if isFile:
            srcRf = open(srcFileName,'rb')#獲取源文件句柄
            while i < srcFileSizeCount:#不斷記錄斷點位置,並對比源文件總計數。以防止出現異常中斷
                i += 1#斷點計數
                if i > breakpointAt:#進入斷點續傳條件
                    line = srcRf.readline() #源文件讀一行
                    obj.sendall(line)# 發給客戶端一行---------------------------------------------------------------send
                    obj.recv(200)#接收一個 get返回信息--------------------------------------------------------------recv
                    breakAtf = open(sizeBreakpointFile,'a')#打開記錄斷點的文件句柄
                    #drcWf = open(drcFileName,'ab')
                    #drcWf.write(line)#寫目標文件
                    s = str(breakpointAt)
                    s = "%s\n"%s
                    #print(breakpointAt)
                    breakAtf.write(s)#寫斷點位置計數
                    breakAtf.close()
                    #drcWf.close()
                    msg = "msg|{}|{}".format(srcFileSizeCount,breakpointAt)#發送 msg|源文件總數|斷點位置
                    obj.sendall(bytes(msg,'utf8'))#----------------------------------------------------------------send
                    obj.recv(200)#----------------------------------------------------------------------------------rece   get
                    word = " [正在傳輸 %s]"%srcFileName
                    self.progressBar(s,srcFileSizeCount,word) #進度條
            srcRf.close()#-----------------------------------------------------------------------------------------------傳輸完成
            lastMsg = "alldone|{}".format(MD5)#-----------------
            obj.sendall(bytes(lastMsg,'utf8'))#------------------------------------------------------------------------send     最後發送源文件的MD5值
完整實例client端
相關文章
相關標籤/搜索