需要帮助在两个套接字之间创建TCP中继
我遇到了以下情况:
SomeServer(S) <-> (C)MyApp(S) <-> (C)User
(S) represents a server socket
(C) represents a client socket
基本上,MyApp 和 SomeServer 之间开始进行通信(SomeServer(S) <-> (C)MyApp),一旦一些身份验证程序成功,MyApp(S) 就开始等待 (C)User 连接。一旦 User 连接上,MyApp 就会把 SomeServer 的数据转发给 User。这个过程是双向的。
我已经成功实现了 SomeServer(S) <-> (C)MyApp 的部分,但我无法让 MyApp(S) <-> (C)User 正常工作。我能做到的是 User 成功连接到 MyApp(S),但无法转发数据!
好的,希望这能让你明白 ;) 现在让我展示一下我的 MyApp 的代码。顺便说一下,SomeServer 和 User 的实现对解决我的问题并不重要,因为这两个部分都无法修改。
我在代码中添加了注释,指出了我遇到问题的地方。哦,我还应该提到,如果有必要,我可以完全放弃“服务器部分”的代码,换成其他代码。这只是一个概念验证(POC),所以我主要关注的是功能的实现,而不是编写高效的代码。感谢你的时间。
''' MyApp.py module '''
import asyncore, socket
import SSL
# Client Section
# Connects to SomeServer
class MyAppClient(asyncore.dispatcher):
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host, port))
connectionPhase = 1
def handle_read(self):
print "connectionPhase =", self.connectionPhase
# The following IF statements may not make sense
# as I have removed code irrelevant to this question
if self.connectionPhase < 3: # authentication phase
data = self.recv(1024)
print 'Received:', data
# Client/Server authentication is handled here
# Everything from this point on happens over
# an encrypted socket using SSL
# Start the RelayServer listening on localhost 8080
# self.socket is encrypted and is the socket communicating
# with SomeServer
rs = RelayServer(('localhost', 8080), self.socket)
print 'RelayServer started'
# connectionPhase = 3 when this IF loop is done
elif self.connectionPhase == 3: # receiving data for User
data = self.recv(1024)
print 'Received data - forward to User:', data
# Forward this data to User
# Don't understand why data is being read here
# when the RelayServer was instantiated above
# Server Section
# Connects to User
class RelayConnection(asyncore.dispatcher):
def __init__(self, client, sock):
asyncore.dispatcher.__init__(self)
self.client = client
print "connecting to %s..." % str(sock)
def handle_connect(self):
print "connected."
# Allow reading once the connection
# on the other side is open.
self.client.is_readable = True
# For some reason this never runs, i.e. data from SomeServer
# isn't read here, but instead in MyAppClient.handle_read()
# don't know how to make it arrive here instead as it should
# be relayed to User
def handle_read(self):
self.client.send(self.recv(1024))
class RelayClient(asyncore.dispatcher):
def __init__(self, server, client, sock):
asyncore.dispatcher.__init__(self, client)
self.is_readable = False
self.server = server
self.relay = RelayConnection(self, sock)
def handle_read(self):
self.relay.send(self.recv(1024))
def handle_close(self):
print "Closing relay..."
# If the client disconnects, close the
# relay connection as well.
self.relay.close()
self.close()
def readable(self):
return self.is_readable
class RelayServer(asyncore.dispatcher):
def __init__(self, bind_address, MyAppClient_sock):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind(bind_address)
self.MyAppClient_sock = MyAppClient_sock
print self.MyAppClient_sock
self.listen(1)
def handle_accept(self):
conn, addr = self.accept()
RelayClient(self, conn, self.MyAppClient_sock)
if __name__ == "__main__":
# Connect to host
# First connection stage
connectionPhase = 1
c = MyAppClient('host', port) # SomeServer's host and port
asyncore.loop()
编辑:
@samplebias 我用你的代码(未显示)替换了我的整个模块,并重新添加了我需要的所有身份验证等部分。
此时,我得到的结果和我之前的代码是一样的。我的意思是,MyApp(或你代码中的服务器)已经连接到 SomeServer,并且数据可以双向传输。到目前为止一切正常。当 User(或客户端应用程序)连接到 localhost 8080 时,这段代码会被执行:
if not self.listener:
self.listener = Listener(self.listener_addr, self)
但是,这段代码没有被执行
# if user is attached, send data
elif self.user:
print 'self.user'
self.user.send(data)
所以,服务器没有把数据转发给用户。我在用户类中添加了打印语句,以查看哪些代码被执行,只有 init 被执行,而 handle_read() 从未运行。
这是为什么呢?
1 个回答
这段代码有点难懂,我相信里面有一些错误。比如在handle_read()这个函数里,你把MyAppClient的原始套接字self.socket
传给了RelayServer。这样一来,MyAppClient和RelayConnection就都在使用同一个套接字了。
与其给原始代码建议修复错误,我自己写了一个例子,这个例子实现了你代码的意图,而且更简洁、更容易理解。我已经测试过它与IMAP服务器的通信,效果不错,但为了简洁省略了一些内容(比如错误处理、在所有情况下正确关闭连接等)。
- 服务器发起与"someserver"的连接。一旦连接成功,它就会启动监听器。
- 监听器在8080端口监听,只接受一个连接,创建一个用户,并将服务器的引用传给它。在用户活跃期间,监听器会拒绝所有其他客户端的连接。
- 用户将所有数据转发给服务器,反之亦然。注释部分指明了应该在哪里插入身份验证的代码。
源代码:
import asyncore
import socket
class User(asyncore.dispatcher_with_send):
def __init__(self, sock, server):
asyncore.dispatcher_with_send.__init__(self, sock)
self.server = server
def handle_read(self):
data = self.recv(4096)
# parse User auth protocol here, authenticate, set phase flag, etc.
# if authenticated, send data to server
if self.server:
self.server.send(data)
def handle_close(self):
if self.server:
self.server.close()
self.close()
class Listener(asyncore.dispatcher_with_send):
def __init__(self, listener_addr, server):
asyncore.dispatcher_with_send.__init__(self)
self.server = server
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
self.bind(listener_addr)
self.listen(1)
def handle_accept(self):
conn, addr = self.accept()
# this listener only accepts 1 client. while it is serving 1 client
# it will reject all other clients.
if not self.server.user:
self.server.user = User(conn, self.server)
else:
conn.close()
class Server(asyncore.dispatcher_with_send):
def __init__(self, server_addr, listener_addr):
asyncore.dispatcher_with_send.__init__(self)
self.server_addr = server_addr
self.listener_addr = listener_addr
self.listener = None
self.user = None
def start(self):
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect(self.server_addr)
def handle_error(self, *n):
self.close()
def handle_read(self):
data = self.recv(4096)
# parse SomeServer auth protocol here, set phase flag, etc.
if not self.listener:
self.listener = Listener(self.listener_addr, self)
# if user is attached, send data
elif self.user:
self.user.send(data)
def handle_close(self):
if self.user:
self.user.server = None
self.user.close()
self.user = None
if self.listener:
self.listener.close()
self.listener = None
self.close()
self.start()
if __name__ == '__main__':
app = Server(('someserver', 143), ('localhost', 8080))
app.start()
asyncore.loop()