2009-09-25 55 views
2

我的類包含一個連接到服務器的套接字。該類的某些方法可能會拋出異常。我正在運行的腳本包含一個外部循環,用於捕獲異常,記錄錯誤並創建一個嘗試重新連接到服務器的新類實例。當你拋出異常時,python對象會發生什麼變化

問題是服務器一次只處理一個連接(按設計)並且「舊」套接字仍處於連接狀態。所以新的連接嘗試掛起腳本。我可以通過強制關閉舊套接字來解決這個問題,但我想知道:爲什麼套接字不能自動關閉?

當它「卡住」時,netstat會顯示連接到端口的兩個套接字。服務器正在等待來自第一個套接字的輸入,但它尚未處理新的套接字。

我對虛擬服務器執行此操作,該虛擬服務器向每個傳入線路回覆「error \ n」。

編輯:請參閱我對Mark Rushakoff's answer below的評論。從異常處理程序中斷言[錯誤] [我隨後捕獲]似乎強制套接字關閉。

import socket 

class MyException(Exception): 
    pass 

class MyClient(object): 

    def __init__(self, port): 
     self.sock = socket.create_connection(('localhost', port)) 
     self.sockfile = self.sock.makefile() 

    def do_stuff(self): 
     self._send("do_stuff\n") 
     response = self._receive() 
     if response != "ok\n": 
      raise MyException() 
     return response 

    def _send(self, cmd): 
     self.sockfile.write(cmd) 
     self.sockfile.flush() 

    def _receive(self): 
     return self.sockfile.readline() 

def connect(): 
    c = MyClient(9989) 
    # On the second iteration, do_stuff() tries to send data and 
    # hangs indefinitely. 
    print c.do_stuff() 

if __name__ == '__main__': 
    for _ in xrange(3): 
     try: 
      connect() 
     except MyException, e: 
      print 'Caught:', e 
      # This would be the workaround if I had access to the 
      # MyClient object: 
      #c.sock.close() 
      #c.sockfile.close() 

編輯:這裏的(醜陋的)服務器代碼:

import socket 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) 
s.bind(('localhost', 9989)) 
s.listen(5) 
(c,a) = s.accept() 
f = c.makefile() 
print f.readline() 
f.write('error\n') 
f.flush() 
(c2,a) = s.accept() 
f = c.makefile() 
print f.readline() 
s.close() 

回答

5

這是垃圾收集的神器。即使對象是超出範圍,它不一定是收集並因此銷燬直到垃圾收集運行發生 - 這不像C++,其中一旦對象失去範圍時調用析構函數。

你或許可以解決這個具體問題,通過改變connect

def connect(): 
    try: 
     c = MyClient(9989) 
     # On the second iteration, do_stuff() tries to send data and 
     # hangs indefinitely. 
     print c.do_stuff() 
    finally: 
     c.sock.close() 
     c.sockfile.close() 

或者,你可以定義__enter____exit__MyClient,並做a with statement

def connect(): 
    with MyClient(9989) as c: 
     print c.do_stuff() 

這是實際上是相同的作爲最後的嘗試。

+0

我不確定這是否有水。我剛剛發現,如果我將try /除了在另一個try/except除AssertionError外並且在內部的except塊中斷言(False),那麼套接字就會被清除。我懷疑它與持有對該對象的引用並因此引用套接字的異常堆棧跟蹤有關? – bstpierre 2009-09-25 02:57:10

+0

你的例子不是一個完整的程序(服務器代碼沒有提供),所以有點難以確認這方面的行爲。 – 2009-09-25 03:01:01

+0

感謝您添加變通方法。我沒有解決它的問題,我只是想了解什麼是對象的引用......現在我必須假設它是追溯。 – bstpierre 2009-09-25 03:03:35

0

好的,這是最終版本。當某些東西變成borked時,顯式關閉套接字對象。

import socket 

class MyException(Exception): 
    pass 

class MyClient(object): 

    def __init__(self, port): 
     self.sock = socket.create_connection(('localhost', port)) 
     self.sockfile = self.sock.makefile() 

    def close(self): 
     self.sock.close() 
     self.sockfile.close() 

    def do_stuff(self): 
     self._send("do_stuff\n") 
     response = self._receive() 
     if response != "ok\n": 
      raise MyException() 
     return response 

    def _send(self, cmd): 
     self.sockfile.write(cmd) 
     self.sockfile.flush() 

    def _receive(self): 
     return self.sockfile.readline() 

def connect(): 
    try: 
     c = MyClient(9989) 
     print c.do_stuff() 
    except MyException: 
     print 'Caught MyException' 
    finally: 
     c.close() 

if __name__ == '__main__': 
    for _ in xrange(2): 
     connect() 
+0

我不確定你爲什麼猶豫現在就解決這個問題?唯一可靠的行爲是使用'finally'或者'with'結構,並且移動代碼以避免寫出正確的行爲似乎是一個奇怪的選擇。 – Kylotan 2009-09-25 09:25:05

+0

現在修復正確的方法。 – bstpierre 2009-09-25 12:57:51

0

通過將相關對象設置爲無,可以標記垃圾收集器進行清理。

1

你真的可以處理connect()中的異常嗎?

我想你應該提供一個MyClient.close()方法,並寫連接()是這樣的:

def connect(): 
    try: 
        c = MyClient(9989) 
        print c.do_stuff() 
    finally: 
        c.close() 

這是完全類比類似文件的對象(with語句)

相關問題