2015-10-23 120 views
1

我要瘋了在python中編寫一個小套接字服務器。一切工作正常,但我注意到,在客戶端剛剛消失的情況下,服務器無法辨別。我通過拉動客戶端和服務器之間的以太網電纜來模擬這一點,關閉客戶端,然後重新插入電纜。服務器從不會聽到客戶端斷開連接,並且將永遠等待,從不允許更多客戶端連接。Python套接字客戶端消失,服務器不能告知

我想我會通過給讀取循環添加一個超時來解決這個問題,以便它每隔10秒嘗試讀取一次。我想也許如果它試圖從套接字讀取它會注意到客戶端丟失。但後來我意識到服務器確實無法知道這一點。

所以我添加了心跳。如果服務器在未讀取的情況下進入10秒鐘,則會將數據發送到客戶端。然而,即使這是成功的(意味着不會拋出任何異常)。所以我能夠讀取和寫入一個不存在的客戶端。有沒有什麼方法可以知道客戶端沒有在客戶端和服務器之間實現某種挑戰/響應協議而走了?這將是一個突破性的變化,我想避免它。

這裏是我的代碼爲這個核心:

def _loop(self): 
     command = "" 
     while True: 
      socket, address = self._listen_socket.accept() 
      self._socket = socket 
      self._socket.settimeout(10) 
      socket.sendall("Welcome\r\n\r\n") 
      while True: 
       try: 
        data = socket.recv(1) 
       except timeout: # Went 10 seconds without data 
        pass 
       except Exception as e: # Likely the client closed the connection 
       break 
      if data: 
       command = command + data 
       if data == "\n" or data == "\r": 
        if len(command.strip()) > 0: 
         self._parse_command(command.strip(), socket) 
         command = "" 
       if data == '\x08': 
        command = command[:-2] 
      else: # Timeout on read 
       try: 
        self._socket.sendall("event,heartbeat\r\n") # Send heartbeat 
       except: 
        self._socket.close() 
        break 

心跳的sendall不會拋出一個異常,recv的唯一拋出超時(或其他異常,如果客戶能夠正確地關閉下正常連接情況)。

任何想法?我錯了,發送給一個沒有ACK的客戶端最終會產生一個異常(我測試了幾分鐘)。

+0

它應該失敗........最終...當系統合理確信沒有可行路線存在。由於可行的鏈路可能涉及撥號調制解調器或信鴿等缺陷,這可能需要很長時間。如您所懷疑的那樣,應用程序級輪詢/確認通常用於在「安靜」間隔期間監視可達性,而不會在對等端之間交換其他數據。 –

+0

另外'服務器從不會聽到客戶端斷開連接,並且將永遠等待,永遠不會允許更多的客戶端連接。' - 指出您的服務器設計存在嚴重問題。一個卡住/ AWOL客戶端不應該阻止他人連接/通信! –

回答

0

您正在觀察的行爲是TCP套接字連接的預期行爲。特別是,通常TCP堆棧無法知道以太網電纜已被拔出或(現在物理上斷開連接的)遠程客戶端程序已關閉;所有它知道的是它已經停止接收來自遠程對等體的確認分組,並且對於所有它知道的分組可能只是由於某個地方的重載路由器而被丟棄,並且該問題將暫時自行解決。鑑於此,TCP在數據包未得到確認時始終執行:它降低了傳輸速率和數據包數量限制,並重新傳輸未確認的數據包,希望它們能夠通過此操作時間。

假設服務器的套接字有待發送的數據,TCP堆棧將最終(即幾分鐘後)確定沒有數據經過足夠長的時間,並且單方面關閉連接。因此,如果您的問題檢測時間只有幾分鐘,那麼避免殭屍連接問題的最簡單方法就是確保週期性地通過TCP連接發送一些心跳數據,正如您所描述的那樣。當TCP堆棧試圖(並反覆失敗)獲取發送和確認的傳出數據時,最終會觸發它關閉連接。

如果你想要比這更快的東西,你需要用超時實現你自己的質詢/響應系統(通過TCP套接字,或通過單獨的TCP套接字,或通過UDP),但請注意,在做所以你很可能自己受到誤報(例如,你最終可能會斷開一個並非實際死亡的TCP連接,而只是由於擁塞導致暫時丟失數據包)。這是否值得權衡取決於你正在編寫什麼類型的程序。 (還要注意,UDP有它自己的問題,特別是如果你希望你的系統跨防火牆工作等)

相關問題