1. 可以鏈接多個客戶端;socket
2. 客戶端長時間未響應可以斷開節省資源;ui
3. 可以羣發,一個用戶發來一條消息,全部客戶端均可以接收;spa
直接使用socket.socket類就能夠實現,可是這樣的話咱們須要本身手動寫監聽,寫accept,把accept單獨放入一個線程,每連入一個線程就新開闢一個線程。線程
這裏咱們選用socketserver這個庫來實現,這樣咱們只須要把核心步驟寫出來便可,其餘的都能自動化幫咱們完成了。code
1 mport socketserver 2 import datetime 3 import threading 4 5 6 class MyHandle(socketserver.BaseRequestHandler): # 每個線程就一個實例 7 def self_server_init(self): 8 if not hasattr(self.server, "clients"): 9 setattr(self.server, "clients", {}) # 若是沒有這個屬性就給他增長一個 10 if not hasattr(self.server, "_lock_clients"): 11 setattr(self.server, "lock_clients", threading.Lock()) 12 self.server.__dict__["hb_interval"] = 10 13 self.server.clients[self.key] = datetime.datetime.now().timestamp() # 記錄時間 14 15 def setup(self): 16 # self init 17 self.event = threading.Event() 18 self.key = self.request, self.event 19 # self.server init 20 self.self_server_init() 21 22 def handle(self): 23 no_hb = set() 24 while not self.event.is_set(): 25 data = self.request.recv(1024) 26 print(data.decode()) 27 if data == b"^hb^": # 收到了心跳包 28 with self.server.lock_clients: 29 self.server.clients[self.key] = datetime.datetime.now().timestamp() 30 if data == b"" or data.strip() == b"quit": 31 with self.server.lock_clients: 32 self.server.clients.pop(self.key, None) # 若是退出就彈出了 33 break # 由於一個線程是一個鏈接 34 35 # 若是沒有退出咱們就刷新時間,也有多是新的鏈接 36 self.server.clients[self.key] = datetime.datetime.now().timestamp() 37 38 # 發送消息的時候檢查是否超時了 39 with self.server.lock_clients: 40 for key, t in self.server.clients.items(): 41 if datetime.datetime.now().timestamp() - t > self.server.hb_interval: # 若是超時了記錄一下這個key 42 no_hb.add(key) # s和e均可哈希 43 continue 44 key[0].send("from {}: {}".format(self.client_address, data.decode()).encode()) 45 for key in no_hb: 46 self.server.clients.pop(key) # 移除沒有心跳的客戶端 47 no_hb.clear() 48 49 def finish(self): 50 super().finish() 51 self.event.set() 52 53 54 server = socketserver.ThreadingTCPServer(("127.0.0.1", 9999), MyHandle) 55 server.serve_forever() # 啓動