【发布时间】:2011-08-08 21:06:36
【问题描述】:
也许这里有人会对这件让我发疯的事情做出回应。
为了简单起见,我正在制作一种代理。每当它接收到某些东西时,它会将所有内容转发到服务器,然后发回响应。所以有一个socket总是监听4557端口的客户端,对于每一个传入的连接,都会在一个随机端口上创建一个新的socket来连接到服务器的4556端口。
客户端 代理 服务器
此外,还有另一个套接字被实例化并侦听来自服务器的请求并转发到相应的客户端。
这是一个例子:
- 客户端 A 连接到端口 4557 上的代理
- 代理在端口 4556 上创建一个到服务器的套接字
- 除此之外,它还会创建一个侦听端口 40100 的套接字
- 客户端发送内容,转发到服务器
- 客户端断开连接。关闭客户端连接和到服务器的套接字
- 一段时间后,服务器在端口 40100 上向代理发送内容
- 所有内容都转发给客户端 A(对应客户端 A 的端口 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。它们分别负责转发给Server,和转发回Client。
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()
您会看到,每当一个连接关闭时,线程就会终止,而另一个连接(客户端/服务器到代理或代理到服务器/客户端)应该在 Proxy.closeConnections() 中关闭
我注意到当 closeConnections() 是“data = self.proxy.conn.recv(BUFFER_SIZE)”时,它运行良好,但是当它在后一条语句之后被调用时,它就出错了。
我wireshark TCP,代理没有发送任何“再见信号”。套接字状态不会进入 TIME_WAIT 或其他状态,它只是保持 ESTABLISHED。
另外,我在 Windows 和 Ubuntu 上对其进行了测试。
- 在 Windows 上完全按照我的解释进行
- 在 Ubuntu 上,它适用于通常(并非总是)2 个连接,并且第三次以完全相同的方式连接到同一客户端到代理时,它再次出现错误,完全按照说明进行。
这是我正在使用的三个文件,以便您查看整个代码。很抱歉,代理文件可能不太容易阅读。应该是一个快速的开发者。
http://hognerud.net/stackoverflow/
提前谢谢.. 这肯定是一件愚蠢的事情。当你看到它时请不要打我太重:(
【问题讨论】:
-
您能否分享您在哪里跟踪打开的客户端套接字列表,以及它们如何与代理服务的代理套接字相关联?我可以花一些时间阅读代码,但这种基本信息会更容易,所以每个人都不会做同样的研究。
-
你为什么要发布应该有效的代码而不是无效的代码来处理打开和关闭有问题的服务器套接字?
-
感谢 cmets。最初的帖子已编辑。