2008-10-07 76 views
43

我在python中編寫了一個簡單的多線程遊戲服務器,爲每個客戶端連接創建一個新的線程。我發現無時不在,服務器會因爲管道故障/ SIGPIPE錯誤而崩潰。當程序嘗試將響應發送回不再存在的客戶端時,我非常肯定它正在發生。如何處理python中的破管(SIGPIPE)?

處理這個問題的好方法是什麼?我的首選解決方案將簡單地關閉與客戶端的服務器端連接並繼續前進,而不是退出整個程序。

PS:This問題/答案以通用的方式處理問題;我應該如何具體解決它?

回答

36

閱讀try:語句。

try: 
    # do something 
except socket.error, e: 
    # A socket error 
except IOError, e: 
    if e.errno == errno.EPIPE: 
     # EPIPE error 
    else: 
     # Other error 
+0

如果我做了一個嘗試:#something except:#anything,它只會捕獲任何東西,而不只是IOErrors? – 2008-10-07 20:28:25

+5

毛毯除外是一個不好的政策。但是,它會抓住任何一種例外。你知道這是一個IOError。處理那個。如果別的東西出現了,找出原因並妥善處理。您不想掩蓋像零除或內存不足等錯誤。 – 2008-10-07 20:46:35

+1

如果您使用的是Python的套接字模塊,您將不會收到IOError異常:您將得到一個socket.error異常。 – mhawke 2008-10-08 00:17:27

3

SIGPIPE(雖然我想,也許你的意思EPIPE?)當您關閉套接字,然後將數據發送給它發生在插座。簡單的解決方案是在嘗試發送數據之前不要關閉套接字。這也可能發生在管道上,但聽起來不像你正在經歷的那樣,因爲它是一個網絡服務器。

您也可以在每個線程的某個頂級處理程序中應用捕獲異常的創可貼。

當然,如果您使用的是Twisted而不是爲每個客戶端連接產生一個新線程,那麼您可能不會遇到此問題。如果多個線程正在處理相同的I/O通道,那麼確定關閉和寫入操作的順序是非常困難的(也許不可能,取決於您的應用程序)。

-3

我的回答是非常接近美國洛特的,但我會更加特別:

try: 
    # do something 
except IOError, e: 
    # ooops, check the attributes of e to see precisely what happened. 
    if e.errno != 23: 
     # I don't know how to handle this 
     raise 

,其中「23」是你從EPIPE得到錯誤號碼。這樣您就不會嘗試處理權限錯誤或其他任何您沒有配備的東西。

47

假設您使用的是標準套接字模塊,您應該捕獲socket.error: (32, 'Broken pipe')異常(不像其他人所建議的那樣是IOError)。這將在您描述的情況下提出,即發送/寫入遠程端已斷開連接的套接字。

import socket, errno, time 

# setup socket to listen for incoming connections 
s = socket.socket() 
s.bind(('localhost', 1234)) 
s.listen(1) 
remote, address = s.accept() 

print "Got connection from: ", address 

while 1: 
    try: 
     remote.send("message to peer\n") 
     time.sleep(1) 
    except socket.error, e: 
     if isinstance(e.args, tuple): 
      print "errno is %d" % e[0] 
      if e[0] == errno.EPIPE: 
       # remote peer disconnected 
       print "Detected remote disconnect" 
      else: 
       # determine and handle different error 
       pass 
     else: 
      print "socket error ", e 
     remote.close() 
     break 
    except IOError, e: 
     # Hmmm, Can IOError actually be raised by the socket module? 
     print "Got IOError: ", e 
     break 

注意,此異常不會始終在第一次寫入提升到一個封閉的插座 - 更通常的第二寫入(除非寫在第一次寫入的字節數比套接字的緩衝區大小)。如果應用程序認爲遠端在第一次寫入時可能已經斷開連接,則需要記住這一點。

通過使用select.select()(或poll),可以減少發生率(但不完全消除)。在嘗試寫入之前,檢查是否準備好從對等端讀取數據。如果select報告有數據可用於從對等套接字讀取,請使用socket.recv()讀取它。如果這返回一個空字符串,遠程對等關閉了連接。因爲這裏仍然存在競爭條件,您仍然需要捕捉並處理異常情況。

扭曲對於這類事情來說很好,但是,聽起來好像你已經寫了一小段代碼。

0

我面對同樣的問題。但我在下次提交相同的代碼時,它正常工作。 第一次就壞了:

$ packet_write_wait: Connection to 10.. port 22: Broken pipe 

它的作品第二次:

[1] Done     nohup python -u add_asc_dec.py > add2.log 2>&1 

我猜測原因可能是有關當前服務器環境。