2011-04-19 106 views
2

也許這裏有人會對這件事做出迴應,這隻會讓我發瘋。Python TCP套接字不能關閉?

爲簡單起見,我正在做一個類型的代理。每當它收到一些東西時,它會將所有內容轉發給服務器,然後發回響應。因此,有一個套接字始終在端口4557上監聽客戶端,並且對於每個傳入連接,在隨機端口上創建一個新套接字以連接到服務器端口4556.

客戶端< ==>代理< == >服務器

而且,它被實例化,監聽來自該服務器的請求,並轉發給相應的客戶端的另一個插座。

下面是一個例子:

  • 客戶端A連接到代理服務器端口4557
  • 代理端口4556
  • 創建socket服務器伴隨着的是,它創建了一個套接字偵聽端口40100
  • 客戶端發送的東西,轉發給服務器
  • 客戶端斷開連接。關閉客戶端連接和套接字服務器
  • 一段時間後,服務器將東西代理端口40100
  • 一切都轉發給客戶端(對應於客戶端端口40100)
  • 等等..

到目前爲止,在我的測試中,我使用一個簡單的python腳本向代理髮送一個唯一的tcp數據包,以及一個顯示接收數據並回顯的轉儲服務器。

所以問題是,當關閉代理連接時,與服務器的連接也應該用「sock.close()」關閉。但它似乎完全被忽略。該套接字保持爲ESTABLISHED。

關於現在的代碼。

的幾個注意事項。

  • DTN和Node分別是服務器和客戶端。
  • 在循環中調用runCallback直到線程死亡。
  • 當線程正在死亡時,會調用finalCallback。
  • 遠程主機(客戶端),代理端口(到服務器)和代理之間的關聯保留在字典中:TCPProxyHostRegister(RemoteHost => Proxy),TCPProxyPortRegister(Port => Proxy),TCPPortToHost(Port => RemoteHost)。

第一類是TCPListenerThread。 它只是偵聽特定端口並實例化代理(每個客戶端=>服務器端和服務器=>客戶端端),並轉發它們之間的連接。

class TCPListenerThread(StoppableThread): 
    def __init__(self, tcp_port): 
     StoppableThread.__init__(self) 

     self.tcp_port = tcp_port 

     self.sock = socket.socket(socket.AF_INET, # Internet 
         socket.SOCK_STREAM) # tcp 
     self.sock.bind((LOCAL_ADDRESS, self.tcp_port)) 

     self.sock.listen(1) 

    def runCallback(self): 
     print "Listen on "+str(self.tcp_port)+".." 
     conn, addr = self.sock.accept() 

     if isFromDTN(addr): 
      tcpProxy = getProxyFromPort(tcp_port) 
      if not tcpProxy: 
       tcpProxy = TCPProxy(host, True) 
     else: 
      host = addr[0] 
      tcpProxy = getProxyFromHost(host) 
      if not tcpProxy: 
       tcpProxy = TCPProxy(host, False) 

     tcpProxy.handle(conn) 

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

現在到了TCP代理: 它相關聯的遠程主機(客戶端)與端口連接到服務器。 如果它是來自新客戶端的連接,它將爲服務器創建一個新偵聽器(請參見上文),並創建一個套接字準備將所有內容轉發到服務器。

class TCPProxy(): 
    def __init__(self, remote, isFromDTN): 
     #remote = port for Server or Remote host for Client 
     self.isFromDTN = isFromDTN 
     self.conn = None 

     #add itself to proxy registries 

     #If listening from a node 
     if not isFromDTN: 
      #Set node remote host 
      self.remoteHost = remote 
      TCPProxyHostRegister[self.remoteHost] = self 

      #Set port to DTN interface + listener 
      self.portToDTN = getNewTCPPort() 
      TCPPortToHost[self.portToDTN] = self.remoteHost 
      newTCPListenerThread(self.portToDTN) 
     #Or from DTN 
     else: 
      self.portToDTN = remote 
      TCPProxyPortRegister[self.portToDTN] = self 

      self.remoteHost = getRemoteHostFromPortTCP(self.portToDTN) 

    def handle(self, conn): 
     print "New connection!" 

     #shouldn't happen, but eh 
     if self.conn != None: 
      self.closeConnections() 

     self.conn = conn 

     #init socket with remote 
     self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
     #self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
     if self.isFromDTN: 
      self.sock.connect((self.remoteHost, 4556)) #TODO: handle dynamic port.. 
     else: 
      self.sock.connect((DTN_Address, DTN_TCPPort)) 

     #handle connection in a thread 
     self.handlerThread = newTCPHandlerThread(self) 
     #handle reply in a therad 
     self.replyThread = newTCPReplyThread(self) 

    def closeConnections(self): 
     try: 
      if self.conn != None: 
       print "Close connections!" 
       self.sock.close() 
       self.conn.close() 
       self.conn = None 
       self.handlerThread.kill() 
       self.replyThread.kill() 
     except Exception, err: 
      print str(err) 
      #pass 

    def forward(self, data): 
     print "TCP forwarding data: "+data 
     self.sock.send(data) 

    def forwardBack(self, data): 
     print "TCP forwarding data back: "+data 
     self.conn.send(data) 

在這個代理類,我實例化兩個類,TCPHandlerThread和TCPReplyThread。他們分別負責轉發到服務器和轉發回客戶端。

class TCPHandlerThread(StoppableThread): 
    def __init__(self, proxy): 
     StoppableThread.__init__(self) 
     self.proxy = proxy 

    def runCallback(self): 
     test = False 
     while 1: 

      data = self.proxy.conn.recv(BUFFER_SIZE)  
      if test: 
       self.proxy.sock.close() 

      test = True 
      if not data: 
       break 
      print "TCP received data:", data 
      self.proxy.forward(data) 
     self.kill() 

    def finalCallback(self): 
     self.proxy.closeConnections() 



class TCPReplyThread(StoppableThread): 
    def __init__(self, proxy): 
     StoppableThread.__init__(self) 
     self.proxy = proxy 

    def runCallback(self): 
     while 1: 
      data = self.proxy.sock.recv(BUFFER_SIZE) 
      if not data:    
       break 
      print "TCP received back data: "+data 
      self.proxy.forwardBack(data) 
     self.kill() 

    def finalCallback(self): 
     self.proxy.closeConnections() 

你看到,每當一個連接被關閉,該線程死亡和其他連接(Client/Server到代理或代理到服務器/客戶端)應Proxy.closeConnections()

我關閉注意到當closeConnections()是「data = self.proxy.conn.recv(BUFFER_SIZE)」時,它很好,但是當它在後面的語句之後被調用時,它就會出錯。

我有wireharked TCP,並且代理不發送任何「再見信號」。套接字狀態不會進入TIME_WAIT或其他什麼,它只是保持ESTABLISHED。

另外,我在Windows和Ubuntu上測試了它。

  • 在Windows上它會完全按照我解釋
  • 在Ubuntu上,它可以很好地用於通常(不總是),2個連接,並且我第三次與同一客戶端完全相同的方式連接到代理服務器,它就像解釋一樣再次出錯。

以下是我使用的三個文件,以便您可以查看整個代碼。對不起,代理文件可能不太容易閱讀。被支持是一個快速開發。

http://hognerud.net/stackoverflow/

在此先感謝.. 這是肯定一些愚蠢的事。當你看到它:(

+0

你能分享你在哪裏跟蹤打開客戶端列表套接字,以及它們如何與代理套接字關聯到代理服務?我可以花一些時間閱讀代碼,但是這種基本信息會讓它更容易,所以每個人都不會做同樣的研究。 – 2011-04-19 18:01:45

+1

爲什麼你發佈的代碼應該是可以工作的,而不是代碼不能,那些處理打開和關閉有問題的服務器套接字的代碼? – 9000 2011-04-19 18:11:49

+0

感謝您的意見。初始文章編輯。 – leyou 2011-04-19 19:21:15

回答

6

首先,我很抱歉,我目前還沒有實際運行和測試你的代碼的時候,請不要打我太用力。

但這個想法來到我的腦海裏,你的問題可能實際上有一些與套接字使用阻塞模式和非阻塞模式有關的問題,在這種情況下,你應該檢查python文檔中的「套接字」模塊幫助,尤其是socket.setblocking()。我的猜測是,proxy.conn.recv()函數只返回,當實際上BUFFER_SIZE字節由套接字接收時。因此線程被阻塞,直到收到足夠的數據,因此套接字不會得到clo SED。

正如我先說,這是目前只是一個猜測,所以請不要投我失望,如果不解決這個問題...

+0

對不起,延遲迴復。其實這解決了我的問題。謝謝! – leyou 2011-04-27 14:04:53

+0

np - 我很高興能幫上忙。 – Dolphin 2011-05-02 05:31:17