Этот пример написан на C #, а не на Java, но концепции обхода NAT не зависят от языка.
См. Сетевую библиотеку Майкла Лидгрена, в которую встроен обход NAT.
Ссылка: http://code.google.com/p/lidgren-network-gen3/
Конкретный файл C #, работающий с NAT Traversal: http://code.google.com/p/lidgren-network-gen3/source/browse/trunk/Lidgren.Network/NetNatIntroduction.cs
Вы отправили правильный процесс. Он будет работать только для 3 из 4 основных типов устройств NAT (я говорю «общий», потому что поведение NAT на самом деле не стандартизировано): полные конусы NAT, ограниченные конусы и Port-Restricted- Конус NAT. Обход NAT не будет работать с симметричными NAT, которые в основном используются в корпоративных сетях для повышения безопасности. Если одна сторона использует Symmetric NAT, а другая - нет, все еще возможно пройти через NAT, но это требует больше догадок. Обратный путь от Symmetric NAT к Symmetric NAT чрезвычайно сложен - вы можете прочитать об этом здесь .
Но на самом деле описанный вами процесс работает точно. Я реализовал это для моей собственной программы для совместного использования экрана (также, к сожалению, в C #). Просто убедитесь, что вы отключили брандмауэр Windows (если вы используете Windows) и сторонние брандмауэры. Но да, я могу с радостью подтвердить, что это сработает.
Разъяснение процесса обхода NAT
Я пишу это обновление, чтобы прояснить процесс обхода NAT для вас и будущих читателей. Надеюсь, это может быть четкое резюме истории и процесса.
Некоторые справочные источники: http://think -like-a-computer.com / 2011/09/16 / types-of-nat / и http://en.wikipedia.org/wiki/Network_address_translation, http://en.wikipedia.org/wiki/IPv4, http://en.wikipedia.org/wiki/IPv4_address_exhaustion.
Исчезли адреса IPv4 с возможностью уникального именования примерно 4,3 миллиарда компьютеров. Умные люди предвидели эту проблему и, среди прочего, изобрели маршрутизаторы для борьбы с исчерпанием адресов IPv4, назначив сеть компьютеров, подключенных к одному общему IP-адресу.
Существуют IP-адреса локальной сети. И затем есть IP-адреса WAN. IP-адреса локальной сети - это IP-адреса локальной сети, которые однозначно идентифицируют компьютеры в локальной сети, например, настольные компьютеры, ноутбуки, принтеры и смартфоны, подключенные к домашнему маршрутизатору. IP-адреса WAN однозначно идентифицируют компьютеры вне локальной сети в глобальной сети, обычно называемой Интернетом. Таким образом, эти маршрутизаторы назначают группе компьютеров 1 WAN IP. Каждый компьютер по-прежнему имеет свой собственный IP-адрес локальной сети. Локальные IP-адреса - это то, что вы видите, когда вы набираете ipconfig
в командной строке и получаете IPv4 Address . . . . . . . . 192.168.1.101
. IP-адреса WAN - это то, что вы видите, когда подключаетесь к cmyip.com
и получаете 128.120.196.204
.
Так же, как радиочастотный спектр выкупается , таким образом, целые диапазоны IP-адресов выкупаются и резервируются агентствами и организациями, , а также номера портов . И снова короткое сообщение о том, что у нас больше нет свободных адресов IPv4.
Какое это имеет отношение к прохождению NAT?Что ж, с тех пор как были изобретены маршрутизаторы, прямые подключения ( сквозное подключение ) были несколько ... невозможны, без нескольких взломов.Если у вас есть сеть из 2 компьютеров (компьютер A и компьютер B), которые совместно используют IP-адрес глобальной сети 128.120.196.204
, к какому компьютеру подключается?Я говорю о внешнем компьютере (скажем, google.com) , инициирующем соединение с 128.120.196.204
.Ответ: никто не знает , как и маршрутизатор, и поэтому маршрутизатор разрывает соединение.Если Компьютер A инициирует соединение, скажем, с google.com
, то это другая история.Затем маршрутизатор запоминает, что компьютер A с LAN IP 192.168.1.101
инициировал подключение к 74.125.227.64
(google.com).Когда пакет запроса компьютера А покидает маршрутизатор, маршрутизатор фактически перезаписывает LAN IP 192.168.1.101
в WAN IP маршрутизатора 128.120.196.204
.Таким образом, когда google.com получает пакет запроса от компьютера A, он видит IP-адрес отправителя, который переписал маршрутизатор, а не IP-адрес локальной сети компьютера A (google.com видит 128.120.196.204
в качестве IP-адреса для ответа).Когда google.com наконец отвечает, пакет достигает маршрутизатора, маршрутизатор запоминает (у него есть таблица состояний), что он ожидал ответа от google.com, и соответствующим образом пересылает пакет на компьютер A.
Другими словами, у вашего роутера нет проблем, когда вы инициируете соединение - ваш роутер не забудет переслать ответный пакет обратно на ваш компьютер (через весь процесс, описанный выше).Но когда внешний сервер инициирует соединение с вами , маршрутизатор не может знать, для какого компьютера было предназначено соединение, поскольку оба компьютера A и B совместно используют IP-адрес глобальной сети 128.120.196.204
...если только нет четкого правила, которое предписывает маршрутизатору пересылать все пакеты, изначально отправляемые на порт назначения X
, а теперь - на компьютер A, порт назначения Y
.Это известно как переадресация портов .К сожалению, если вы думаете об использовании переадресации портов для своих сетевых приложений, это не практично, поскольку ваши пользователи могут не понимать, как включить его, и могут неохотно включать его, если считают, что это угроза безопасности. UPnP просто означает технологию, которая позволяет программно включать переадресацию портов .К сожалению, если вы думаете об использовании UPnP для перенаправления ваших сетевых приложений, это также не практично, поскольку UPnP не всегда доступен, а когда он есть, он может не включаться по умолчанию.
Таккакое решение тогда?Решение состоит в том, чтобы либо проксировать весь ваш трафик через ваш собственный компьютер (который вы предварительно тщательно настроили, чтобы обеспечить глобальный доступ), либо найти способ обойти систему.Первое решение (я считаю) называется TURN и волшебным образом решает все проблемы с подключением за счет предоставления ферме серверов с доступной пропускной способностью.Второе решение называется NAT traversal, и это то, что мы будем изучать дальше.
Ранее я описывал процесс подключения внешнего сервера (например, google.com) к 128.120.196.204
.Я сказал, что без наличия у роутера особых правил, позволяющих понять, на какой компьютер перенаправлять запрос на соединение Google, роутер просто отбросит соединение.Это был обобщенный сценарий, и он не является точным, поскольку существуют разные типы NAT.(Примечание. Маршрутизатор - это фактическое физическое устройство, которое вы можете разместить на полу. NAT (преобразование сетевых адресов) - это программный процесс, запрограммированный в маршрутизаторе, который помогает сохранять адреса IPv4 как деревья).Таким образом, в зависимости от , который NAT использует маршрутизатор, сценарии подключения могут быть разными.Маршрутизатор может даже объединять процессы NAT.
Существует четыре типа NAT со стандартизованным поведением: NAT с полным конусом, NAT с ограниченным конусом, NAT с ограниченным портом и симметричный NAT. Помимо этих типов, могут быть другие типы NAT с нестандартизированным поведением, но это более редко.
Примечание: я не очень знаком с NAT ... кажется, что есть много способов взглянуть на маршрутизаторы, и информация в Интернете очень распространена по этой теме. Википедия говорит, что классифицировать NAT по полным, ограниченным и ограниченным по порту конусам несколько устарело. Есть нечто, называемое статическими и динамическими NAT ... просто куча различных концепций, которые я не могу согласовать вместе. Тем не менее, следующая модель работала для моего собственного приложения. Вы можете узнать больше о NAT, прочитав ссылки ниже и выше, а также в этом посте. Я не могу больше о них писать, потому что не очень разбираюсь в них.
Надеемся, что некоторые сетевые гуру исправят / добавят ввод, чтобы мы все могли больше узнать об этом загадочном процессе.
Чтобы ответить на ваш вопрос о сборе внешнего IP и порта каждого клиента:
Заголовки всех пакетов UDP структурированы одинаково с одним исходным IP-адресом и одним исходным портом. Заголовки пакетов UDP не содержат «внутренний» IP-адрес источника и «внешний» IP-адрес источника. Заголовки пакетов UDP содержат только один исходный IP. Если вы хотите получить «внутренний» и «внешний» исходный IP-адрес, вам фактически нужно отправить внутренний исходный IP-адрес как часть вашей полезной нагрузки. Но это не похоже на то, что вам нужен внутренний исходный IP-адрес и порт. Похоже, вам нужен только внешний IP и порт, как указано в вашем вопросе. Это означает, что ваше решение состоит в том, чтобы просто прочитать исходный IP-адрес и портировать пакет, как поля.
Два сценария ниже (они больше ничего не объясняют):
Связь по локальной сети
Компьютер A имеет IP-адрес локальной сети 192.168.1.101. Компьютер B имеет IP-адрес локальной сети 192.168.1.102. Компьютер A отправляет пакет с порта 3000 на компьютер B с портом 6000. Исходный IP-адрес пакета UDP будет 192.168.1.101. И это будет единственный IP. «Внешний» здесь не имеет контекста, потому что сеть является чисто локальной сетью. В этом примере глобальная сеть (например, Интернет) не существует. Что касается портов, то, поскольку я не уверен насчет NAT, я не уверен, что порт, указанный на пакете, будет 3000. Устройство NAT может перезаписать порт пакета с 3000 до чего-то случайного типа 49826. В любом случае, вы должны использовать любой порт, указанный на пакете, для ответа - это то, что вы должны использовать для ответа. Таким образом, в этом примере связи по локальной сети вам нужно отправить только один IP - IP-адрес локальной сети, потому что это все, что имеет значение. Вам не нужно беспокоиться о порте - маршрутизатор позаботится об этом за вас. Когда вы получаете пакет, вы собираете только IP и порт, просто считывая их из пакета.
WAN Связь
Компьютер A имеет IP-адрес локальной сети 192.168.1.101. Компьютер B имеет IP-адрес локальной сети 192.168.1.102. И компьютер A, и компьютер B будут использовать IP-адрес WAN 128.120.196.204. Сервер S - это сервер, глобально доступный компьютер, скажем, на сервере Amazon EC2, с IP-адресом глобальной сети 1.1.1.1. Сервер S может иметь IP-адрес локальной сети, но это не имеет значения. Компьютер B тоже не имеет значения.
Компьютер A отправляет пакет с порта 3000 на сервер S. При выходе из маршрутизатора исходный IP-адрес пакета локальной сети с компьютера A перезаписывается на WAN IP-адрес маршрутизатора. Маршрутизатор также перезаписывает исходный порт от 300 до 32981. Что видит Сервер S с точки зрения внешнего IP-адреса и порта? Сервер S видит 128.120.196.204 в качестве IP-адреса, а не 192.168.1.101, а Сервер S видит 32981 в качестве порта, а не 3000. Хотя это не исходные IP-адреса и порты компьютера A, используемого для отправки пакета, это правильные IP-адреса и порты для ответа. Когда вы получаете пакет, вы можете знать только WAN IP и переписанный порт. Если это то, что вы хотите (вы запрашивали только внешний IP и порт), то все готово. В противном случае, если вы также хотите получить внутренний IP-адрес отправителя, вам нужно будет передать эти обычные данные , отделяя от вашего заголовка.
Код:
Как указано выше (ниже Чтобы ответить на ваш вопрос о сборе внешнего IP-адреса), чтобы собрать внешний IP-адрес и порт каждого клиента, вы просто считываете их из пакета. Каждая отправленная датаграмма всегда имеет исходный IP-адрес и исходный порт отправителя; вам даже не нужен необычный специальный протокол, потому что эти два поля всегда включены - каждый отдельный пакет UDP должен, по определению, иметь эти два поля.
// Java language
// Buffer for receiving incoming data
byte[] inboundDatagramBuffer = new byte[1024];
DatagramPacket inboundDatagram = new DatagramPacket(inboundDatagramBuffer, inboundDatagramBuffer.length);
// Source IP address
InetAddress sourceAddress = inboundDatagram.getAddress();
// Source port
int sourcePort = inboundDatagram.getPort();
// Actually receive the datagram
socket.receive(inboundDatagram);
Поскольку getAddress()
и getPort()
могут возвращать порт назначения или порт источника, в зависимости от того, что вы задали, на клиентском (отправляющем) компьютере вызовите setAddress()
и setPort()
на сервер (получающий ), и на сервере (получателе) вызовите setAddress()
и setPort()
назад к клиенту (отправителю). Должен быть способ сделать это в receive()
. Пожалуйста, уточните, действительно ли это (getAddress()
и getPort()
не возвращает исходный IP-адрес и порт, который вы ожидаете) является вашим настоящим препятствием. Это предполагает, что сервер является «стандартным» сервером UDP (это не сервер STUN).
Дальнейшее обновление:
Я прочитал ваше обновление о ", как использовать STUN для получения IP-адреса и порта от одного клиента и передачи его другому "? Сервер STUN не предназначен для обмена конечными точками или выполнения NAT-обхода. Сервер STUN предназначен для определения вашего общедоступного IP-адреса, общего порта и типа устройства NAT (будь то NAT с полным конусом, NAT с ограниченным конусом или NAT с ограниченным портом). Я бы назвал сервер-посредник, отвечающий за обмен конечными точками и выполнение фактического обхода NAT, «представителем». В моем личном проекте мне фактически не нужно использовать STUN для выполнения обхода NAT. Мой «представитель» (сервер-посредник, который представляет клиентов A и B) - это стандартный сервер, который прослушивает дейтаграммы UDP. Поскольку оба клиента A и B регистрируются в интродьюсере, интродюсер считывает их общедоступный IP-адрес, порт и частный IP-адрес (если они находятся в локальной сети). Открытый IP считывается из заголовка дейтаграммы, как и для всех стандартных дейтаграмм UDP. Частный IP записывается как часть полезной нагрузки дейтаграммы, а интродьюсер просто считывает его как часть полезной нагрузки. Итак, что касается полезности STUN, вам не нужно полагаться на STUN для получения общедоступного IP-адреса и публичного порта каждого из ваших клиентов - любой подключенный сокет может сказать вам об этом. Я бы сказал, что STUN полезен только для определения того, под каким типом устройства NAT находится ваш клиент, чтобы вы знали, выполнять ли обход NAT (если тип устройства NAT - Full-Cone, Restricted или Port-Restricted) или выполнять проксирование всего TURN-трафика (если тип устройства NAT симметричный).
Пожалуйста, уточните ваш контрольно-пропускной пункт: если вы хотите получить советы по передовым методам разработки протокола обмена сообщениями приложений и рекомендации по чтению полей из полученных сообщений упорядоченным и систематическим образом (на основе комментария, который вы разместили ниже), не могли бы вы поделитесь своим текущим методом?