經過非IO阻塞模型實現ftp併發的小代碼

  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

相關文章
相關標籤/搜索