python сервер-клиент обход nat

Я кое-что прочитал об обходе UDP NAT, и я достаточно уверен, что понимаю основы, но я все еще борюсь с реализацией.

В моем проекте есть глобально доступный сервер и клиенты за nat. Это игра, в которой базовый запрос join_game отправляется от клиента к серверу, а затем сервер отправляет обновления каждый интервал. Я тестировал дома и забыл, что у меня включен DMZ на моем маршрутизаторе, поэтому он работал нормально. Я отправил это некоторым друзьям для проверки, и они не могут получать обновления с сервера.

Вот текущая методология, все пакеты UDP:

  • Клиент открывает сокет и отправляет запрос на присоединение к серверу.
  • Сервер получает запрос и ответный адрес сообщения и отвечает клиенту: да, вы можете присоединиться, и, кстати, я буду отправлять обновления на ваш ответный ip/порт, который выглядит так.
  • Клиент перехватывает ответ, затем закрывает сокет и запускает класс потокового прослушивателя UDP для прослушивания порта ответа, о котором нам сообщил сервер.
  • Затем клиент перехватывает обновления сервера, которые переполняются, и обрабатывает их по мере необходимости. Время от времени клиент открывает новый сокет и отправляет UDP-пакет с обновлением на сервер (какие клавиши нажаты и т. д.).

Насколько я понимаю, ответный адрес, который получает сервер, должен иметь правильный порт для прохождения клиентского nat. И отправка пакетов туда достаточно часто будет поддерживать действие правила обхода nat.

Этого не происходит. Клиент отправляет запрос на присоединение и получает ответ сервера на этом сокете. Но когда я закрываю сокет, а затем запускаю потоковый прослушиватель UDP на порту ответа, он ничего не улавливает. Это почти как если бы правило обхода действительно только для одного пакета ответа.

Я могу включить код, если это необходимо, но, честно говоря, это несколько слоев классов и объектов, и он делает то, что я описал выше. Код работает, когда я включаю DMZ, но не когда он выключен.

Я включу некоторые фрагменты, представляющие интерес.

Вот серверный обработчик запроса на присоединение. client_address передается из потокового обработчика и является атрибутом SocketServer.BaseRequestHandler, self.client_address. Без разбора, просто передано.

def handle_player_join(self, message, reply_message, client_address):

        # Create player id
        player_id = create_id()

        # Add player to the connected nodes dict
        self.modules.connected_nodes[player_id] = client_address

        # Create player ship entity
        self.modules.players[player_id] = self.modules.factory.player_ship( position     = (320, 220),
                                                                            bearing      = 0,
                                                                          )

        # Set reply to ACK, and include the player id and listen port
        reply_message.body                  = Message.ACK
        reply_message.data['PLAYER_ID']     = player_id
        reply_message.data['LISTEN_PORT']   = client_address[1]

        print "Player Joined :"+str(client_address)+", ID: "+str(player_id)

        # Return reply message
        return reply_message

Друг упомянул, что, возможно, когда я отправляю запрос на присоединение, а затем получаю ответ, я не должен закрывать сокет. Держите этот сокет живым и сделайте его слушателем. Я не уверен, что закрытие сокета как-то повлияет на обход nat, и я не знаю, как создать потоковый прослушиватель udp, который берет уже существующий сокет, не переписывая всю чертову штуку (чего я бы предпочел не делать). ).

Нужны идеи или информация?

Ваше здоровье


person Oliver    schedule 21.09.2015    source источник
comment
Обычно NAT выполняется маршрутизатором/брандмауэром. Клиент не имеет ничего общего с NAT, на самом деле клиент просто знает, куда отправлять пакеты, вот и все. Я не знаю, какая у вас сеть? Может быть, маршрутизатор/брандмауэр на стороне вашего друга неправильно настроен для выполнения NAT? Какой тип NAT? Как вы тестировали дома? Были ли сервер и клиент в локальной сети с маршрутизатором между ними? Im not convinced closing the socket will have any effect on the nat traversal В случае PAT действует!   -  person ρss    schedule 21.09.2015
comment
У моих друзей есть стандартный роутер/модем, который вы получаете бесплатно с любым интернет-пакетом. Обычный интернет в модем, основная локалка в доме. То же, что и моя установка, за исключением того, что я включил DMZ для своей машины. Отключение DMZ привело к тому, что он перестал работать. Питон был упакован в исполняемый файл pyinstaller, и когда вы запускаете его, Win7 запрашивает разрешение на его подключение к сети (которое они приняли). Я также запускал его исключительно из скриптов на своей машине без DMZ, и он не работает. В наших сетевых настройках нет ничего необычного :(   -  person Oliver    schedule 21.09.2015
comment
Что вы имеете в виду, в случае PAT это влияет?   -  person Oliver    schedule 21.09.2015
comment
Трудно исследовать вашу проблему, так как это скорее сетевой вопрос, чем вопрос программирования. Включение DMZ может привести к некоторым изменениям в правиле фильтрации модема/брандмауэра, и ваш код работает, поскольку сетевые подключения разрешены. PAT обычно выполняется на основе IP + Port number. А Socket = IP+Port no., так что теперь вы видите, что порта нет. эффекты ПАТ. Когда порт закрывается, соответствующая запись PAT удаляется из брандмауэра. Попробуйте superuser.com для лучшего ответа на ваш вопрос!   -  person ρss    schedule 21.09.2015
comment
хм, я не знаком с PAT. Это полностью работает на клиентском ПК? Итак, вы теряете сопоставление, когда закрываете сокет?   -  person Oliver    schedule 21.09.2015
comment
PAT — это разновидность NAT. Это выполняется брандмауэром/маршрутизатором. Когда брандмауэр/маршрутизатор отслеживает все преобразования NAT. Поэтому всякий раз, когда клиент закрывает соединение, брандмауэр/маршрутизаторы знают об этом и закрывают или удаляют соответствующую трансляцию NAT. Итак, как я уже говорил, клиенту не нужно знать ни о каком NAT. Это функция брандмауэра/маршрутизатора.   -  person ρss    schedule 21.09.2015
comment
Когда вы закроете UDP-сокет, будет ли проинформирован PAT брандмауэра/маршрутизатора? Я думал, что UDP-сокеты просто передают и отправляют данные, без постоянных соединений или состояний.   -  person Oliver    schedule 21.09.2015
comment
Давайте продолжим обсуждение в чате.   -  person ρss    schedule 21.09.2015


Ответы (1)


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

Не закрывайте сокет, из которого вы отправили пакеты на сервер. Когда вы создаете сокет, он привязывается к частному IP-порту. Когда вы отправляете пакет на сервер, этот IP:Port будет преобразован в один общедоступный IP:Port вашего NAT. Теперь, когда вы закрываете этот сокет, данные с вашего сервера сначала поступают на ваш общедоступный IP-адрес: порт NAT и перенаправляются на ваш частный IP-адрес: порт. Но поскольку ваш сокет закрыт, никто не получит эти данные. Теперь у сервера нет возможности узнать, что вы создали новый сокет с новым частным IP:портом, потому что вы никогда не отправляли пакет на свой сервер после создания этого нового сокета. Так что не закрывайте старый сокет. Попробуйте послушать с этим старым в треде. Или вы можете отправить пакет на сервер из нового сокета, сообщив ему ваш новый переведенный общедоступный IP-адрес: порт. Таким образом, этот сервер может отправлять свои данные на этот новый общедоступный IP-адрес: порт, который, в свою очередь, будет перенаправлен на ваш новый частный IP-адрес: порт.

Закройте сокет, но повторно используйте тот же порт. Когда вы закроете свой старый сокет и создадите новый сокет, привяжите его к порту, к которому был привязан старый сокет. Это не изменит общедоступный IP-адрес NAT: порт, и данные с вашего сервера не будут прерваны.

person Tahlil    schedule 22.09.2015