Рассмотрим следующую ситуацию в Windows:
- сокет прослушивания 1 имеет установленную опцию
SO_EXCLUSIVEADDRUSE
и привязывается к порту - прослушивать сокет 1 получает входящее соединение, которое он принимает для создания подключенного сокета
- сокет прослушивания 1 закрыт, но подключенный сокет остается открытым (в некотором смысле, возможно, ESTABLISHED или TIME_WAIT)
- Для сокета прослушивания 2 задана опция
SO_EXCLUSIVEADDRUSE
, и он пытается подключиться к тому же порту, что и первый прослушивающий сокет. Успешно?
В сети нет тонны информации об этом, но большинство из них согласны с тем, что на последнем этапе bind
всегда будет вызывать ошибку, потому что SO_EXCLUSIVEADDRUSE
предотвращает прослушивание сокета 2 и подключенного сокета, совместно использующего порт. Это важно, потому что SO_EXCLUSIVEADDRUSE
обеспечивает некоторые нетривиальные преимущества безопасности, но если это нарушает повторную привязку порта, трудно решить, использовать это или нет.
текущие документы MSDN ясно, что, по крайней мере, в некоторых случаях затяжное соединение может привести к сбою второго bind
, хотя они нечетко говорят о том, что именно считается "активным соединением":
И наоборот, сокет с установленным SO_EXCLUSIVEADDRUSE не обязательно может быть повторно использован сразу после закрытия сокета. Например, если прослушивающий сокет с установленным SO_EXCLUSIVEADDRUSE принимает соединение и затем закрывается, другой сокет (также с SO_EXCLUSIVEADDRUSE) не может привязаться к тому же порту, что и первый сокет, до тех пор, пока исходное соединение не станет неактивным.
libuv явно предпочитает не использовать SO_EXCLUSIVEADDRUSE
- несмотря на преимущества безопасности - потому что они говорят, что это мешает TIME_WAIT
обработке.
И этот microsoft.com Сообщение в блоге от 2005 года содержит некоторые дополнительные сведения, утверждая, что подключенный сокет может предотвратить SO_EXCLUSIVEADDRUSE
повторное связывание по крайней мере в некоторых случаях (но не TIME_WAIT
):
Хотя опция SO_EXCLUSIVEADDRUSE чрезвычайно полезна, есть важное предостережение. Если по крайней мере одно соединение, которое возникло или было принято на порте, привязанном к монопольному доступу, активно, то все привязки к этому порту завершатся ошибкой. В этом случае «соединение» определяется как сокет, который явно подключен к одноранговому узлу через connect, WSAConnect или ConnectEx с установленным эксклюзивным флагом или соединение, возвращаемое из прослушивающего сокета (например, от accept, WSAAccept или AcceptEx ), для которого установлена эксклюзивная опция (на прослушивающем сокете). Активный порт для TCP определяется как в состояниях ESTABLISHED, FIN_WAIT, FIN_WAIT_2 или LAST_ACK.
Поэтому я написал небольшой скрипт, чтобы попробовать на себе:
https://gist.github.com/njsmith/8770bed5bbf2154940e8e3e7762e4ac3
и действительно, когда я запускаю его в Windows 10, я получаю:
rebind with existing listening socket: failed
details: OSError(10048, 'Only one usage of each socket address (protocol/network address/port) is normally permitted', None, 10048, None)
rebind with live connected sockets: succeeded
rebind with TIME_WAIT socket: succeeded
Итак, мой предварительный вывод заключается в том, что MSDN и все остальные ошибаются: пока исходный прослушивающий сокет закрыт, SO_EXCLUSIVEADDRUSE
действительно позволяет повторно привязать порт, с которым связаны оставшиеся подключенные сокеты, будь то состояние ESTABLISHED или TIME_WAIT или что-то еще. Этого все хотят, и это здорово. Предположительно, в 2005 году это было не так, но где-то между тем и сейчас они это исправили.
Вопросы:
- Верна ли моя интерпретация этих результатов?
- Есть ли еще один случай, когда мне не хватает предыдущего соединения, когда предыдущее соединение могло привести к сбою
SO_EXCLUSIVEADDRUSE
bind
, а обычноеbind
- сбою? (Я ожидал, что если СОЗДАННЫЕ сокеты не вызывают проблемы, значит, ничего не происходит, но я могу что-то упустить.) - Самое главное: когда они внесли изменения? Например, я тестировал Windows 10, но если бы мой код по какой-то причине нуждался в ориентировании на Windows Vista, сработало бы это или нет? Или это всегда так работало, а документация всегда была неправильной?