TCP-сокет Python не закрывается? - PullRequest
3 голосов
/ 19 апреля 2011

Возможно, у кого-то здесь будет ответ на эту вещь, которая просто сводит меня с ума.

Чтобы упростить это, я делаю своего рода прокси.Всякий раз, когда он что-то получает, он пересылает все на сервер и отправляет ответ.Таким образом, существует один сокет, всегда прослушивающий порт 4557 для клиентов, и для каждого входящего соединения на произвольном порту создается новый сокет для подключения к порту 4556 сервера.

Клиенты <==> Прокси-сервер <==> Сервер

Также есть еще один сокет, который создается и прослушивает запросы, поступающие с сервера и пересылаемые соответствующему клиенту.

Вот пример:

  • Клиент A подключается к прокси-серверу через порт 4557
  • Прокси-сервер создает сокет для сервера на порту 4556
  • Наряду с этим он создает сокет, прослушивающий порт 40100
  • Клиент отправляет вещи, перенаправленные на сервер
  • Клиент отключается.Закрыть клиентское соединение и сокет к серверу
  • Через некоторое время сервер отправляет данные на прокси-сервер через порт 40100
  • Все пересылается клиенту A (порт 40100 соответствует клиенту A)
  • И так далее.

До сих пор в своих тестах я использовал простой скрипт на 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()

Вы видите, что всякий раз, когда соединение закрывается, поток умирает, а другое соединение (клиент / сервер с прокси или прокси сСервер / Клиент) должны быть закрыты в Proxy.closeConnections ()

Я заметил, что когда closeConnections () имеет значение «data = self.proxy.conn.recv (BUFFER_SIZE)», все идет хорошо, но когда он вызываетсядаже сразу после последнего утверждения все идет не так.

Я подключил протокол TCP, и прокси-сервер не посылает никакого "пока сигнала".Состояние сокета не переходит в TIME_WAIT или что-либо еще, оно просто остается УСТАНОВЛЕННЫМ.

Кроме того, я протестировал его в Windows и Ubuntu.

  • В Windows все идет именно так, как я объяснил
  • В Ubuntu он обычно работает нормально (не всегда), 2 соединения, и в третий раз, когда я подключаюсь к одному и тому же клиенту точно так же, как прокси, он снова идет не так, как описано.

Вот три файла, которые я использую, чтобы вы могли взглянуть на весь код.Мне жаль, что файл прокси не может быть действительно легко читаемым.Предполагалось, что это быстрая разработка.

http://hognerud.net/stackoverflow/

Заранее спасибо ..Это конечно что-то глупое. Пожалуйста, не бей меня слишком сильно, когда увидишь: (

1 Ответ

7 голосов
/ 20 апреля 2011

Во-первых, я сожалею, что в настоящее время у меня нет времени, чтобы на самом деле запустить и протестировать ваш код.

Но мне пришла в голову мысль, что ваша проблема может действительно иметь какое-то отношение к использованию режима блокировки противнеблокирующий режим на розетке.В этом случае вы должны обратиться к справке модуля "socket" в документации по python, особенно socket.setblocking ().

Я предполагаю, что функция proxy.conn.recv () возвращает только тогда, когда на самом деле BUFFER_SIZEбайт, где получен сокетом.Из-за этого поток блокируется до тех пор, пока не будет получено достаточно данных, и, следовательно, сокет не закроется.

Как я уже сказал вначале, это всего лишь предположение, поэтому, пожалуйста, не голосуйте за меня, если ононе решает проблему ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...