python併發網絡通訊模型

併發網絡通訊模型

常見網絡模型

  • 循環服務器模型:循環接受客戶端請求,處理請求.同一時刻只能處理一個請求,處理完畢後在處理下一個

優勢:實現簡單,佔用資源少服務器

缺點:沒法同時處理多個客戶端請求網絡

適用狀況:處理的任務能夠很快完成,客戶端無需長期佔用服務端程序.UDP比TCP更適合循環多線程

  • 多進程/線程網絡併發模型:每當一個客戶端鏈接服務器,就建立一個新的進程/線程爲該客戶端服務,客戶端退出時在銷燬該進程/線程

優勢:能同時知足多個客戶端長期佔有服務端需求,能夠處理各類請求併發

缺點:資源消耗較大異步

適用狀況:客戶端同時鏈接量較少,須要處理行爲較複雜狀況socket

  • IO併發模型:利用IO多路複用,異步IO等技術,同時處理多個客戶端IO請求

優勢:資源消耗少,能同時高效處理多個IO行爲ide

缺點:只能處理併發產生的IO事件,沒法處理CPU計算函數

適用狀況:HTTP請求,網絡傳輸等都是IO行爲ui

基於fork的多進程網絡併發模型

實現步驟spa

  1. 建立監聽套接字
  2. 等待接受客戶端請求
  3. 客戶端鏈接建立新的進程處理客戶端請求
  4. 原進程繼續等待其餘客戶端鏈接
  5. 若是客戶端退出,則銷燬對應的進程

fork代碼示例:

 1 from socket import *
 2 import os
 3 import signal
 4 
 5 ADDR = ('127.0.0.1',8080)
 6 
 7 def han(c):
 8     while True:
 9         data = c.recv(1024).decode()
10         if not data:
11             break
12         print(data)
13         c.send(b'OK')
14 #建立套接字監聽
15 s = socket()
16 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
17 s.bind(ADDR)
18 s.listen(5)
19 
20 signal.signal(signal.SIGCHLD,signal.SIG_IGN)
21 print("端口鏈接 8080...")
22 
23 while True:
24     #循環等待客戶端鏈接
25     try:
26         c,addr = s.accept()
27         print("鏈接中...",addr)
28     except KeyboardInterrupt:
29         os._exit(0)
30     except Exception as e:
31         print(e)
32         continue
33 
34 #建立新進程
35     pid = os.fork()
36     if pid == 0:
37         #子進程處理具體的客戶端
38         s.close()
39         han(c)  #具體處理請求
40         os._exit(0) #子進程處理請求後銷燬
41 
42     else:
43         pass
View Code

基於threading的多線程網絡併發

 實現步驟

  1. 建立監聽套接字
  2. 等待接收客戶端請求
  3. 客戶端鏈接建立新的線程處理客戶端請求
  4. 主線程繼續等待其餘客戶端鏈接
  5. 若是客戶端退出,則對應分支線程退出

threading代碼示例:

 1 from socket import *
 2 from threading import Thread
 3 import os
 4 
 5 ADDR = ('0.0.0.0',8888)
 6 
 7 # 客戶端處理函數,循環收發消息
 8 def handle(c):
 9     while True:
10         data = c.recv(1024).decode()
11         if not data:
12             break
13         print(data)
14         c.send(b'OK')
15 
16 # 建立監聽套接字
17 s = socket()
18 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
19 s.bind(ADDR)
20 s.listen(5)
21 
22 print("Listen the port 8888....")
23 
24 while True:
25     # 循環等待客戶端鏈接
26     try:
27         c,addr = s.accept()
28         print("Connect from",addr)
29     except KeyboardInterrupt:
30         os._exit(0)
31     except Exception as e:
32         print(e)
33         continue
34 
35     # 建立新的線程處理請求
36     client = Thread(target=handle,args=(c,))
37     client.setDaemon(True)
38     client.start()
View Code

 

ftp文件服務器

小練習

功能

  1. 分爲服務端和客戶端,要求能夠有多個客戶端同時操做
  2. 客戶端能夠查看服務器文件庫中有什麼文件
  3. 客戶端能夠從文件庫中下載文件到本地
  4. 客戶端能夠上傳一個本地文件到文件庫
  5. 使用print在客戶端打印命令輸入提示,引導操做

ftp服務端代碼示例:

  1 from socket import *
  2 from threading import Thread
  3 import os,sys
  4 import time
  5 
  6 # 全局變量
  7 HOST = '0.0.0.0'
  8 PORT = 8080
  9 ADDR = (HOST,PORT)
 10 FTP = "/home/tarena/FTP/" # 文件庫路徑
 11 
 12 # 功能類 (線程類)
 13 # 查文檔, 下載,上傳
 14 class FTPServer(Thread):
 15     def __init__(self,connfd):
 16         super().__init__()
 17         self.connfd = connfd
 18 
 19     # 處理文件列表
 20     def do_list(self):
 21         # 獲取文件列表
 22         files = os.listdir(FTP)
 23         if not files:
 24             self.connfd.send("文件庫爲空".encode())
 25             return
 26         else:
 27             self.connfd.send(b'OK')
 28             time.sleep(0.1)
 29         # 拼接文件
 30         filelist = ''
 31         for file in files:
 32             filelist += file + '\n'
 33         self.connfd.send(filelist.encode())
 34 
 35     def do_get(self,filename):
 36         try:
 37             f = open(FTP+filename,'rb')
 38         except Exception:
 39             # 文件不存在
 40             self.connfd.send('文件不存在'.encode())
 41             return
 42         else:
 43             self.connfd.send(b'OK')
 44             time.sleep(0.1)
 45 
 46         # 發送文件
 47         while True:
 48             data = f.read(1024)
 49             if not data:
 50                 time.sleep(0.1)
 51                 self.connfd.send(b'##')
 52                 break
 53             self.connfd.send(data)
 54 
 55     def do_put(self,filename):
 56         if os.path.exists(FTP+filename):
 57             self.connfd.send("文件已存在".encode())
 58             return
 59         else:
 60             self.connfd.send(b'OK')
 61         # 接收文件
 62         f = open(FTP + filename,'wb')
 63         while True:
 64             data = self.connfd.recv(1024)
 65             if data == b'##':
 66                 break
 67             f.write(data)
 68         f.close()
 69 
 70     # 循環接受來自客戶端的請求
 71     def run(self):
 72         while True:
 73             request=self.connfd.recv(1024).decode()
 74             if not request or request == 'Q':
 75                 return # 線程退出
 76             elif request == 'L':
 77                 self.do_list()
 78             elif request[0] == 'G':
 79                 filename = request.split(' ')[-1]
 80                 self.do_get(filename)
 81             elif request[0] == 'P':
 82                 filename = request.split(' ')[-1]
 83                 self.do_put(filename)
 84 
 85 # 啓動函數
 86 def main():
 87     # 建立監聽套接字
 88     s = socket()
 89     s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
 90     s.bind(ADDR)
 91     s.listen(5)
 92 
 93     print("Listen the port 8080....")
 94 
 95     while True:
 96         # 循環等待客戶端鏈接
 97         try:
 98             c, addr = s.accept()
 99             print("Connect from", addr)
100         except KeyboardInterrupt:
101             os._exit(0)
102         except Exception as e:
103             print(e)
104             continue
105 
106         # 建立新的線程處理請求
107         client = FTPServer(c)
108         client.setDaemon(True)
109         client.start()
110 
111 main()
View Code

 

ftp客戶端代碼示例:

  1 import time
  2 from socket import *
  3 import sys
  4 
  5 # 服務器地址
  6 ADDR = ('127.0.0.1',8080)
  7 
  8 # 文件處理類
  9 class FTPClient:
 10     # 全部函數都使用sockfd,因此把它變爲屬性變量
 11     def __init__(self,sockfd):
 12         self.sockfd = sockfd
 13 
 14     def do_list(self):
 15         self.sockfd.send(b'L') # 發送請求
 16         # 等待回覆 (服務端可否知足請求)
 17         data = self.sockfd.recv(128).decode()
 18         if data == 'OK':
 19             # 一次性接收全部文件
 20             data = self.sockfd.recv(4096)
 21             print(data.decode())
 22         else:
 23             print(data)
 24 
 25     def do_quit(self):
 26         self.sockfd.send(b'Q') # 退出請求
 27         self.sockfd.close()
 28         sys.exit("謝謝使用")
 29 
 30     def do_get(self,filename):
 31         # 發送請求
 32         self.sockfd.send(('G '+filename).encode())
 33         # 等待回覆
 34         data = self.sockfd.recv(128).decode()
 35         if data == 'OK':
 36             f = open(filename,'wb')
 37             # 循環接收內容,寫入文件
 38             while True:
 39                 data = self.sockfd.recv(1024)
 40                 if data == b'##': # 發送完成
 41                     break
 42                 f.write(data)
 43             f.close()
 44         else:
 45             print(data)
 46 
 47     def do_put(self,filename):
 48         try:
 49             f = open(filename,'rb')
 50         except Exception as e:
 51             print("該文件不存在")
 52             return
 53         # 發送請求
 54         filename = filename.split('/')[-1]
 55         self.sockfd.send(('P '+filename).encode())
 56         # 等待反饋
 57         data = self.sockfd.recv(128).decode()
 58         if data == 'OK':
 59             while True:
 60                 data = f.read(1024)
 61                 if not data:
 62                     time.sleep(0.1)
 63                     self.sockfd.send(b'##')
 64                     break
 65                 self.sockfd.send(data)
 66             f.close()
 67         else:
 68             print(data)
 69 
 70 # 啓動函數
 71 def main():
 72     sockfd = socket()
 73     try:
 74         sockfd.connect(ADDR)
 75     except Exception as e:
 76         print(e)
 77         return
 78 
 79     ftp = FTPClient(sockfd) # 實例化對象,用於調用功能
 80     # 循環發送請求給服務器
 81     while True:
 82         print("""\n
 83           =========Command============
 84           ****       list        ****
 85           ****    get   file     ****
 86           ****    put   file     ****
 87           ****       quit        ****
 88           ============================
 89         """)
 90         cmd = input("輸入命令:")
 91         if cmd.strip() == 'list':
 92             ftp.do_list()
 93         elif cmd.strip() == 'quit':
 94             ftp.do_quit()
 95         elif cmd[:3] == 'get':
 96             filename = cmd.split(' ')[-1]
 97             ftp.do_get(filename)
 98         elif cmd[:3] == 'put':
 99             filename = cmd.split(' ')[-1]
100             ftp.do_put(filename)
101         else:
102             print("請輸入正確命令")
View Code
相關文章
相關標籤/搜索