優勢:實現簡單,佔用資源少服務器
缺點:沒法同時處理多個客戶端請求網絡
適用狀況:處理的任務能夠很快完成,客戶端無需長期佔用服務端程序.UDP比TCP更適合循環多線程
優勢:能同時知足多個客戶端長期佔有服務端需求,能夠處理各類請求併發
缺點:資源消耗較大異步
適用狀況:客戶端同時鏈接量較少,須要處理行爲較複雜狀況socket
優勢:資源消耗少,能同時高效處理多個IO行爲ide
缺點:只能處理併發產生的IO事件,沒法處理CPU計算函數
適用狀況:HTTP請求,網絡傳輸等都是IO行爲ui
實現步驟spa
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
實現步驟
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()
ftp文件服務器
小練習
功能
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()
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("請輸入正確命令")