【问题标题】:Need help creating a TCP relay between two sockets需要帮助在两个套接字之间创建 TCP 中继
【发布时间】:2011-03-13 05:02:12
【问题描述】:

我有以下情况:

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 代码。顺便说一句,SomeServerUser 的实现与解决我的问题相关,因为两者都不能修改。

我已对我的代码进行了注释,指出我遇到问题的地方。哦,我还应该提到,如果有必要,我可以为其他代码废弃整个“服务器部分”。这是一个 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(或代码中的 Server)连接到 SomeServer 并来回传递数据。到目前为止一切都很好。当用户(或客户端应用程序)连接到 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)

因此,服务器没有将数据中继给用户。我在整个 User 类中添加了打印语句以查看运行的内容,而 init 是唯一的。 handle_read() 从不运行。

这是为什么?

【问题讨论】:

  • 您应该查看PEP8,如果您遵循它,您的代码不会破坏 SO 的代码标记;-)

标签: python sockets asynchronous asyncore


【解决方案1】:

代码有点难以理解,我敢肯定有一些错误。为了 在 handle_read() 中,您将 MyAppClient 的原始套接字 self.socket 传递给 中继服务器。最终 MyAppClient 和 RelayConnection 都在同一个套接字上工作。

而不是试图建议对我放在一起的原始代码进行错误修复 一个示例,它可以满足您的代码意图,并且更清晰,更易于遵循。 我已经测试过它与 IMAP 服务器对话并且它可以工作,但省略了一些 为简洁起见(错误处理、所有情况下的正确 close() 处理等)。

  • Server 启动到“someserver”的连接。一旦连接 它启动 Listener
  • Listener 监听 8080 端口,只接受 1 个连接,创建一个 User, 并将其传递给 Server 的引用。 Listener 拒绝所有其他 用户处于活动状态时的客户端连接。
  • 用户将所有数据转发到服务器,反之亦然。厘米 指明应在何处插入身份验证。

来源:

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()

【讨论】:

  • 谢谢你的榜样!请参阅我上面帖子的编辑。
  • @yonatan 当我对您的 SomeServer 和 User 协议不太了解时,很难提供帮助。您能否在连接设置后提供有关在每个消息上交换的消息的更多信息。例如,当客户端连接到端口 8080 时,您的服务器是否应该向它发送问候消息?如果是这样,Listener.handle_accept 应该在构造 User 之后执行self.server.user.send(greeting_data)
  • 抱歉回复晚了。你的一个班轮正是缺少的!我无法寻求更好的帮助。您的示例代码完美无缺!非常感谢您解决了我 2 周以来一直在努力解决的问题 :D :D
  • @yonatan 不客气,我很高兴听到你取得了进展。 :)
猜你喜欢
  • 2021-12-19
  • 1970-01-01
  • 2020-01-06
  • 1970-01-01
  • 2010-09-29
  • 2013-07-09
  • 1970-01-01
  • 1970-01-01
  • 2016-02-11
相关资源
最近更新 更多