• Python自动化开发从浅入深-进阶(socketServer)


    socketserver:
       --- 每一个连接过来都会单独创建一个线程。
       ---socketserver处理的线程数目与服务器资源配置有关
    socketserver有四个基本的类:
      --- TCPServer
      ---UDPServer
      ---UnixStreamServer
      ---UnixDatagramServer
    这四个类的同步请求,每一个请求只有完成后才能进行下一个请求。不适合进行长时间的大数据计算,所以需要单独创建进程或线程来处理每个请求。ForkingMixIn和ThreadingMixIn通常被用于支持异步的行为。
    5个类的继承关系如下图:

     

    我们常用的类是:

    ThreadingTCPServer

    ThreadingUDPServer

    ForkingTCPServer

    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()

     完整实例:

    #!/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 : [{}] 
    the 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
    =================') ## ------------------------------------welcome  <send > 1
    
                #self.request.sendall(bytes('Welcome to use FTP
    =================','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("
    ssh 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
    "%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
    "%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
    "%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 = '
    %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
    "%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 = '
    %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
    "%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
    "%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端
  • 相关阅读:
    vue-router HTML5 History 模式(转自官网)
    使用fastjson解析数据后导致顺序改变问题
    Mybatis一对多或多对多只能查出一条数据解决策略
    mybatis 学习教程
    Mybatis根据List批量查询List结果
    @Param注解的用法解析
    关于mybatis使用小于号大于号出错的解决方案
    if test表达式逻辑判断不能用&&
    MyBatis中传入参数parameterType类型详解
    MyBatis 判断条件为等于的问题
  • 原文地址:https://www.cnblogs.com/whiggzhaohong/p/5321457.html
Copyright © 2020-2023  润新知