1 import os 2 import time 3 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 4 import socket 5 import selectors 6 7 class selectFtpServer:#服務端建立一個類 8 9 def __init__(self):#對象初始化的定義 10 self.dic = {}#定義一個空字典 11 self.hasReceived=0#定義一個接受值的大小 12 self.sel = selectors.DefaultSelector()#實例化一個selectors對象 13 self.create_socket()#調用創建鏈接函數 14 self.handle()#調用handle函數。 15 16 def create_socket(self): 17 server = socket.socket() 18 server.bind(("127.0.0.1",8885)) 19 server.listen(5) 20 server.setblocking(False)#非IO阻塞模型 21 self.sel.register(server, selectors.EVENT_READ, self.accept) 22 #註冊一個監聽對象,若是server變化執行accept函數。 23 print("服務端已開啓,等待用戶鏈接...") 24 25 def handle(self): 26 while True: 27 events = self.sel.select()#監聽,將變化的內容賦值 28 for key, mask in events:#循環發生變化的對象 29 callback = key.data#將該對象的方法賦值 30 callback(key.fileobj, mask)#執行這個方法並傳參 31 32 def accept(self,sock, mask): 33 conn, addr = sock.accept() 34 print("from %s %s connected"%addr) 35 self.sel.register(conn, selectors.EVENT_READ, self.read) 36 #註冊conn,若是發生變化執行read函數 37 self.dic[conn] = {}#將conn傳入字典,將一個空字典做爲值 38 39 def read(self, conn, mask):#read函數具體執行上傳和下載的功能 40 try: 41 if not self.dic[conn] :#若是這個conn不在字典裏,說明這是第一次接收到該客戶端消息 42 data = conn.recv(1024)#接收1024的內容 43 cmd,filename,filesize = str(data, encoding='utf-8').split('|')#將內容以管道符分割,分別賦值 44 self.dic={conn:{"cmd": cmd, "filename": filename,"filesize": int(filesize)}}#將取到的內容放到字典裏 45 if cmd == 'put':#若是cmd爲put 46 conn.send(bytes("OK",encoding='utf8'))#回一個返回值 47 if self.dic[conn]['cmd'] == 'get':#若是傳來的命令是get 48 file = os.path.join(BASE_DIR,"download",filename)#拼接文件路徑 49 if os.path.exists(file):#若是文件存在 50 fileSize = os.path.getsize(file)#文件大小等於filesize 51 send_info = '%s|%s'%('YES',fileSize)#將文件大小等放入事先定製好的格式當中。 52 conn.send(bytes(send_info, encoding='utf8'))#發送這個文件信息 53 else: 54 send_info = '%s|%s'%('NO',0)#若是文件不存在,將消息賦值 55 conn.send(bytes(send_info, encoding='utf8'))#發送 56 else: 57 if self.dic[conn].get('cmd',None):#若是conn在字典裏,取出cmd,若是沒有則返回None 58 cmd=self.dic[conn].get('cmd')#將get到的內容賦值給cmd 59 if hasattr(self, cmd):#若是cmd這個函數存在 60 func = getattr(self,cmd)#獲取這個函數並賦值 61 func(conn)#加上參數,調用執行 62 else: 63 print("error cmd!")#若是這個函數不存在,打印error 64 conn.close()#關閉鏈接 65 else: 66 print("error cmd!") 67 conn.close() 68 69 except Exception as e:#異常處理 70 print('error', e) 71 self.sel.unregister(conn)#解除註冊 72 conn.close() 73 74 def put(self, conn):#上傳 75 76 fileName = self.dic[conn]['filename']#字典的文件名賦值 77 fileSize = self.dic[conn]['filesize']#字典的文件大小賦值 78 path = os.path.join(BASE_DIR,"upload",fileName)#文件拼接 79 recv_data = conn.recv(1024)#收1024 80 self.hasReceived += len(recv_data)#已經收到的 81 82 with open(path, 'ab') as f:#作收文件內容的操做 83 f.write(recv_data) 84 if fileSize == self.hasReceived:#若是收到的大小等於實際大小 85 if conn in self.dic.keys():#若是這個conn在字典中存在則清空 86 self.dic[conn] = {} 87 print("%s上傳完畢!"%fileName) 88 89 def get(self,conn):#下載 90 filename = self.dic[conn]['filename']#取文件名 91 path = os.path.join(BASE_DIR,"download",filename)#拼接路徑 92 if str(conn.recv(1024), 'utf-8') == "second_active":#收文件 93 with open(path, 'rb') as f: 94 for line in f: 95 conn.send(line) 96 self.dic[conn] = {}#請空字典 97 print('文件下載完畢!') 98 99 100 if __name__ == '__main__': 101 102 selectFtpServer()
上面是server端代碼,主要就是經過socket和selectors來實現,能夠多加練習。服務器
1 import socket 2 import os,sys 3 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 4 5 class selectFtpClient: 6 7 def __init__(self): 8 self.args=sys.argv 9 if len(self.args)>1: 10 self.port=(self.args[1],int(self.args[2]))#若是運行的時候後面有參數 11 else: 12 self.port=("127.0.0.1",8885)#默認 13 self.create_socket()#建立鏈接函數 14 self.command_fanout()#經過反射獲取函數的函數 15 16 def create_socket(self): 17 try: 18 self.sk = socket.socket() 19 self.sk.connect(self.port) 20 print('鏈接FTP服務器成功!') 21 except Exception as e: 22 print("error: ",e) 23 24 def command_fanout(self): 25 while True: 26 cmd = input('>>>').strip() 27 if cmd == 'exit()': 28 break 29 cmd,file = cmd.split() 30 if hasattr(self, cmd): 31 func = getattr(self, cmd) 32 func(cmd,file) 33 else: 34 print('調用錯誤!') 35 36 def put(self,cmd,file): 37 38 if os.path.isfile(file): 39 fileName= os.path.basename(file) 40 fileSize = os.path.getsize(file) 41 fileInfo ='%s|%s|%s'%(cmd,fileName,fileSize) 42 self.sk.send(bytes(fileInfo, encoding='utf8')) 43 recvStatus = self.sk.recv(1024) 44 print('recvStatus', recvStatus) 45 hasSend = 0 46 if str(recvStatus, encoding='utf8') == "OK": 47 with open(file, 'rb') as f: 48 while fileSize > hasSend : 49 contant = f.read(1024) 50 recv_size = len(contant) 51 self.sk.send(contant) 52 hasSend += recv_size 53 s=str(int(hasSend/fileSize*100))+"%" 54 print("正在上傳文件:"+fileName+" 已經上傳:"+s) 55 print('%s文件上傳完畢' % (fileName,)) 56 else: 57 print('文件不存在') 58 59 60 if __name__ == '__main__': 61 62 selectFtpClient()
上面爲客戶端代碼socket