也許這裏有人會對這件事做出迴應,這隻會讓我發瘋。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/
在此先感謝.. 這是肯定一些愚蠢的事。當你看到它:(
你能分享你在哪裏跟蹤打開客戶端列表套接字,以及它們如何與代理套接字關聯到代理服務?我可以花一些時間閱讀代碼,但是這種基本信息會讓它更容易,所以每個人都不會做同樣的研究。 – 2011-04-19 18:01:45
爲什麼你發佈的代碼應該是可以工作的,而不是代碼不能,那些處理打開和關閉有問題的服務器套接字的代碼? – 9000 2011-04-19 18:11:49
感謝您的意見。初始文章編輯。 – leyou 2011-04-19 19:21:15