1. 程式人生 > >python-selctors實現檔案上傳

python-selctors實現檔案上傳

服務端程式碼:程式目錄server/server.py   上傳檔案目錄:server/upload

import os
import time
import socket
import selectors                     #封裝了一些相應的操作

BASE_DIR=os.path.dirname(os.path.abspath(__file__))

#第二步:
class selectFtpServer:
    def __init__(self):
        self.dic = {}                            #
建立空字典 self.hasReceived=0 self.sel = selectors.DefaultSelector() #通過selectors模組下的DefaultSelector這個類拿I/O多路方法拿到例項物件變成例項變數self.sel self.create_socket() #create_socket()是建立socket物件函式完成繫結功能 self.handle() #handle()函式完成迴圈監聽 #
create_socket函式是用來建立socket物件完成繫結功能 def create_socket(self): server = socket.socket() #建立socket物件賦值server server.bind(('192.168.1.160', 8080)) #繫結 server.listen(5) #監聽 server.setblocking(False) #
設定非阻塞 self.sel.register(server, selectors.EVENT_READ,self.accept) #有了server物件通過self.sel例項變數register註冊完成繫結功能(server跟self.accept做繫結) print("服務端已啟動,等待客戶端連線.....") #handle()函式完成迴圈監聽 def handle(self): while True: #第一步:程式啟動後走while迴圈 #第七步:執行完accept函式後到這裡 #所有操作圍繞一個物件self.sel例項變數核心物件展開的 events = self.sel.select() #第二步:呼叫self.sel例項變數,監聽的內容server封裝到events物件裡 #第八步:如果客戶端發過來此刻監聽的內容就有變化有倆個物件server和conn封裝到events物件裡 for key, mask in events: #第三步:for迴圈events(可迭代物件)拿到key和mask #第九步:for迴圈events(可迭代物件)拿到key和mask callback = key.data #第四步:當前key.data是self.accept函式賦值給callback #第十步:當前key.data是read函式賦值給callback #key.fileobj是拿到的監聽的物件。 callback(key.fileobj, mask) #第五步:執行callback執行accpt()函式 #第十一步:執行callback執行read()函式裡面放到的之前連結相應的檔案描述符conn #print('當前監聽的物件:', key.fileobj) #第六步:accept函式接收連線服務端的客戶端資訊 def accept(self,sock,mask): #接收sock和mask conn, addr = sock.accept() #sock.accept接收此時連結服務端的socket物件拿到conn和addr #print('accepted', conn, 'from', addr) print("from %s %s connected" %addr) conn.setblocking(False) #設定成非阻塞 self.sel.register(conn, selectors.EVENT_READ, self.read) #把conn跟read做繫結後程序返回到handle()函式裡的events繼續監聽 #print(conn) self.dic[conn] = {} #在空字典裡進行了conn賦值,self.dic={conn:{},} #第十二步:read函式返回給客戶端ok def read(self,conn, mask): #接收了conn和mask try: #加異常防止客戶端突然斷開 if not self.dic[conn]: #判斷self.dic[conn]裡面是否是空字典,如果是空字典,代表第一次進來 data = conn.recv(1024) #conn接收了客戶端發來的資料 cmd,filename,filesize = str(data,encoding='utf-8').split('|') #把接收到客戶端發來的包解開拿到cmd,filename,filesize個資訊 self.dic={conn:{"cmd" : cmd, "filename" : filename, "filesize" : int(filesize)}} #把拿到的cmd,filename,filesize資訊放到self.dic字典裡去後程式返回到handle()函式裡的events繼續監聽 if cmd == 'put': #如果接收的資訊是put conn.send(bytes("OK",encoding='utf8')) #給客戶端返回一條資料 if self.dic[conn]['cmd'] == 'get': file = os.path.join(BASE_DIR,"upload",filename) if os.path.exists(file): fileSize = os.path.getsize(file) send_info = '%s|%s' %('YES',fileSize) conn.send(bytes(send_info, encoding='utf8')) else: send_info = '%s|%s' %('NO',0) conn.send(bytes(send_info, encoding='utf8')) else: #如果不是空字典代表不是第一次進來 if self.dic[conn].get('cmd',None): #對接收的命令進行分發判斷是put還是get cmd=self.dic[conn].get('cmd') if hasattr(self, cmd): #如果cmd=put呼叫put函式,如果是cmd=get函式呼叫get函式 func = getattr(self,cmd) func(conn) else: print("error cmd!") conn.close() else: print("error cmd!") conn.close() except Exception as e: print('斷開的客戶端資訊是:', conn) self.sel.unregister(conn) #如果沒有接收到資料做一個關閉解除 conn.close() #put上傳函式 def put(self, conn): fileName = self.dic[conn]['filename'] fileSize = self.dic[conn]['filesize'] path = os.path.join(BASE_DIR,"upload",fileName) #拿到要接收的資訊 #print(fileName,fileSize,path) recv_data = conn.recv(1024) #接收客戶端上傳的資料1024位元組 self.hasReceived += len(recv_data) #把接收的資料累加到變數self.hasReceived with open(path, 'ab') as f: #開啟檔案 f.write(recv_data) #把接收的資料寫到檔案裡去 if fileSize == self.hasReceived: #判斷檔案大小跟接收大小是否一樣 if conn in self.dic.keys(): #如果檔案大小跟接收大小一樣清空字典 self.dic[conn] = {} print("%s 上傳完畢!" %fileName) if __name__=='__main__': selectFtpServer() #第一步:例項化觸發selectFtpServer這個類

客戶端程式碼:目錄結果:/client/clenb.py

import socket
import os,sys
BASE_DIR=os.path.dirname(os.path.abspath(__file__))

class selectFtpClient:
    def __init__(self):
        self.args=sys.argv                              #sys.argv在命令列輸入的引數,第一個引數預設檔名,第二個引數跟IP地址和埠
        if len(self.args)>1:                            #如果大於1把第二個引數倆個值賦值給port
            self.port=(self.args[1],int(self.args[2]))
        else:
            self.port=("192.168.1.160",8080)               #如果沒有第二個引數預設取這個
        self.create_socket()                             #
        self.command_fanout()                            #進行命令分發

    #create_socket函式建立socket物件連線服務端
    def create_socket(self):
        try:
            self.sk = socket.socket()
            self.sk.connect(self.port)
            print('連線FTP伺服器成功!')
        except Exception as e:
            print("eroor:",e)

    #command_fanout()函式進行命令分發
    def command_fanout(self):
        while True:
            cmd = input('>>>').strip()   #引導使用者輸入上傳還是下載
            if cmd == 'exit()':
                break
            cmd,file = cmd.split()        #把輸入的命令分開進行反射
            if hasattr(self,cmd):
                func = getattr(self,cmd)
                func(cmd,file)
            else:
                print('呼叫錯誤!')

    #put()上傳函式
    def put(self,cmd,file):

        if os.path.isfile(file):                            #判斷本地檔案是否存在
            fileName = os.path.basename(file)                #取出檔案的名字
            fileSize = os.path.getsize(file)                 #取出檔案的大小
            fileInfo = '%s|%s|%s'%(cmd,fileName,fileSize)  #給檔名字大小打包成fileInf
            self.sk.send(bytes(fileInfo, encoding='utf8'))  #呼叫send方法把fileInf發給服務端
            recvStatus = self.sk.recv(1024)                  #接收服務端返回的OK內容
            print('recvStatus' , recvStatus)
            hasSend = 0
            if str(recvStatus, encoding='utf8') == "OK":   #如果接收到服務端返回的OK
                with open(file, 'rb') as f:                #開啟檔案
                    while fileSize > hasSend :              #迴圈的去上傳檔案
                        contant = f.read(1024)
                        recv_size = len(contant)
                        self.sk.send(contant)
                        hasSend += recv_size
                        s=str(int(hasSend/fileSize*100))+"%"
                        print("正在上傳檔案: "+fileName+" 已經上傳:" +s)
                print('%s檔案上傳完畢' % (fileName,))
        else:
            print('檔案不存在')

    #get()下載函式
    def get(self,cmd,file):
        pass

if __name__=='__main__':
    selectFtpClient()