несколько TCP-серверов на одном порту

Это выглядит очень странно для меня. Я могу запустить несколько серверов TCP на одном и том же порту.

Я использую библиотеку Apache MINA со следующим кодом:

IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.bind(new InetSocketAddress(80));

Порт 80 уже используется другой программой. Но я не получил исключения «Адрес уже используется». С netstat я вижу следующее:

C:\>netstat -oan |find /i "LIST"
  TCP    0.0.0.0:80             0.0.0.0:0              LISTENING       2220
  TCP    0.0.0.0:80             0.0.0.0:0              LISTENING       904
  TCP    0.0.0.0:135            0.0.0.0:0              LISTENING       840

Может ли кто-нибудь объяснить мне такое поведение?

ОС: Виндовс 7.

Спасибо.


person sasha_trn    schedule 26.11.2012    source источник
comment
Что такое процессы 2220 и 904? И использовался ли порт 80 до того, как вы привязались к нему? Или ваша привязка привела к тому, что она была разделена двумя процессами, тогда как до того, как она была закрыта? Вы звоните setReuseAddress?   -  person David Schwartz    schedule 26.11.2012
comment
@trashgod По этой ссылке нет ничего, что объясняло бы такое поведение. Этого не должно быть.   -  person user207421    schedule 27.11.2012
comment
2220 — это javaw (мое приложение). 904 - это скайп (раньше был запущен). Я не вызывал setReuseAddress.   -  person sasha_trn    schedule 29.11.2012
comment
Я думаю, что более одного приложения могут прослушивать входящие соединения, но когда входящее соединение, наконец, есть, подключиться может только одно.   -  person cxxl    schedule 12.12.2012
comment
Вы нашли Святой Грааль. Наконец-то мы можем запускать веб-серверы, не нуждаясь в отдельном IP-адресе для каждого сертификата или TLS 1.2 с SNI... Ладно, если честно, мне этот вывод кажется очень неправильным. Два процесса, прослушивающие один и тот же хост, порт и протокол, не должны быть возможны.   -  person bikeshedder    schedule 02.02.2013
comment
Как я объяснил в своем ответе и комментариях ниже, это возможно в Windows, однако для этого все равно должно требоваться SO_REUSEADDR, поэтому такое поведение все еще вызывает недоумение, поскольку OP утверждает, что не указал эту опцию. Смотрите мой ответ для более подробной информации.   -  person Cartroo    schedule 02.02.2013


Ответы (1)


Обычно только один процесс может прослушивать TCP-порт в Windows или любой другой ОС (по крайней мере, основных). В Windows вы ожидаете получить код ошибки 10048, если два процесса совместно используют порт. Это не будет применяться, если процессы привязаны к разным адресам интерфейса (даже если один привязан к INADDR_ANY, а другой привязан к определенному адресу, они не конфликтуют). Кроме того, это не применяется, если SO_REUSEADDR установлен на втором сокете.

Поскольку оба процесса привязаны к INADDR_ANY, а вы утверждаете, что ваш процесс не имеет установленного SO_REUSEADDR, это загадка. Насколько я могу судить, есть три возможности:

  1. Что-то в базовой библиотеке устанавливает SO_REUSEADDR по умолчанию.
  2. Второй сокет был фактически открыт позже, и это тот, который указывает SO_REUSEADDR.
  3. В слое сокетов Windows есть ошибка, которая позволила это сделать.

Я понимаю, что идеального программного обеспечения не существует, но я действительно не решаюсь выбрать третий вариант, особенно если вы можете легко воспроизвести его. Я бы посоветовал внимательно посмотреть вывод netstat до и после запуска вашего процесса и посмотреть, существует ли другой слушатель до этого. Кроме того, попробуйте определить другой процесс и посмотреть, связан ли он (для этого вы можете включить столбец PID в диспетчере задач).

ИЗМЕНИТЬ

Комментатор ниже напомнил мне, что я должен указать, что поведение SO_REUSEADDR действительно отличается на разных платформах. Windows позволяет новым сокетам принудительно привязываться к тому же порту, что и другие сокеты прослушивания, с неопределенным поведением, если оба сокета являются TCP, как обсуждалось здесь. На практике второй сокет, вероятно, «крадет» адрес, но официальная линия, по-видимому, такова, что поведение не определено:

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

Linux (и другие варианты Unix) не позволит двум сокетам TCP использовать один и тот же порт, если старый все еще прослушивает. В этом случае SO_REUSEADDR разрешает связывание нового сокета только в том случае, если старый находится в состоянии TIME_WAIT (и, возможно, в состояниях FIN_WAIT и CLOSE_WAIT, мне нужно это проверить).

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

Я не собираюсь указывать, какая платформа является «правильной», но, безусловно, поведение Windows привело к проблемам с безопасностью, поэтому они придумали опцию SO_EXCLUSIVEADDRUSE для предотвращения принудительной привязки других сокетов. Я также, кажется, придерживаюсь мнения, что версию для Windows следует рассматривать как совершенно другой вариант с другим поведением, который просто имеет то же имя.

person Cartroo    schedule 02.02.2013
comment
4. В netstat есть ошибка, которая показывает это ошибочно. Вы не можете привязать сокет TCP к одному и тому же IP-порту с SO_REUSEADDR или без него. Это для UDP. - person user207421; 02.02.2013
comment
По крайней мере, в Windows вы можете привязаться к тому же порту, если используете SO_REUSEADDR, даже если первый сокет активен, но не определено, какой сокет получает соединение. Взгляните на в этом MSDN и прокрутите вниз до раздела Использование SO_EXCLUSIVEADDRUSE и посмотрите на таблицу разрешенных пар bind() вызовов. В тексте упоминается, что это относится к XP и более ранним версиям, но если вы прочитаете дальше, вы увидите, что это только потому, что Vista и расширяет те же проверки для приложений с двойным стеком (IPv4 и IPv6). - person Cartroo; 02.02.2013
comment
Дополнительная цитата со страницы, которую я только что связал, которая описывает поведение с SO_REUSEADDR под Windows: После того, как второй сокет успешно привязан, поведение всех сокетов, привязанных к этому порту, неопределенно. Например, если все сокеты на одном и том же порту предоставляют службу TCP, нельзя гарантировать, что любые входящие запросы на TCP-соединение через этот порт будут обработаны правильным сокетом — поведение недетерминировано. - person Cartroo; 02.02.2013