Привет всем! Недавно я спросил этот вопрос. После полезных ответов я узнал, что сеть по своему замыслу делает невозможным прямое различие устройств, подключенных к одному и тому же маршрутизатору.
Так, например, приведенный ниже код никогда не может быть на 100% функциональным...
require('http').createServer((req, res) => {
let uniqueDeviceId = getUniqueDeviceId(req);
}).listen(80, '<my public ip>', 511);
... потому что нет способа гарантированно реализовать getUniqueDeviceId
. Он не может просто вернуть req.connection.remoteAddress
, так как несколько устройств могут иметь один и тот же адрес.
Учитывая это, мой вопрос: Как res.end(...)
всегда реагирует на правильное устройство??
Например. рассмотрим этот простой сервер:
require('http').createServer((req, res) => {
res.end(req.url);
}).listen(80, '{{my public ip}}', 511);
Возьмите два разных устройства, подключенных к одному и тому же IP-адресу. Каждое устройство использует свой собственный URL:
http://{{my public ip}}/device1
http://{{my public ip}}/device2
Теперь оба этих устройства бомбардируют свой URL тысячами запросов в секунду.
В ответ Устройство 1 всегда будет получать /device1
, а Устройство 2 всегда будет получать /device2
, даже с такой конфигурацией, которая должна привести к условиям гонки!
Несмотря на то, что я не может определить уникальный идентификатор устройства напрямую, res.end
всегда будет соответствовать соответствующему req
— он никогда не ошибется и не отправит ответ на какой-то другой req
, даже если этот другой req
был получен почти в одно и то же время, от тот же IP-адрес. Для меня это доказательство того, что на каком-то уровне, более низком, чем Nodejs, можно определить уникальные идентификаторы устройств.
Если это так, то на каком уровне архитектуры HTTP мы получаем гарантию того, что ответ всегда будет возвращаться к его первоначальному запрашивающему? И как это реализовано?
«Для меня это доказательство того, что на каком-то уровне, более низком, чем Nodejs, можно определить уникальные идентификаторы устройств».
-- Нет, определяется не "уникальный идентификатор устройства", а определяется уникальная комбинация "IP - порт".
Например, в вашем случае Устройство 1 и Устройство 2 скрыты за маршрутизатором, что делает их IP-адрес идентичным (IP-адрес маршрутизатора) вашему веб-серверу. Однако, несмотря на то, что у них один и тот же IP, их порты разные.
Для устройства 1, с точки зрения вашего веб-сервера Node.js, сокет (комбинация SourceIP/Port - DestinationIP/Port) может быть:
{{RouterIP}}:{{PortX}} - {{Node.js serverIP}}:{{Node.js serverPort}}
Для устройства 2, с точки зрения вашего веб-сервера Node.js, сокет может быть:
{{RouterIP}}:{{PortY}} - {{Node.js serverIP}}:{{Node.js serverPort}}
Таким образом, несмотря на то, что Устройство 1 и Устройство 2 имеют один и тот же IP-адрес, ответ HTTP будет возвращен на соответствующий порт маршрутизатора — ответ для устройства 1 будет возвращен на порт X маршрутизатора, а ответ для устройства 2 будет возвращен на порт Y.
Затем внутри маршрутизатора есть отображение, такое как:
{{PortX}} - {{Device 1's private IP}}:{{Device 1's Port}}
{{PortY}} - {{Device 2's private IP}}:{{Device 2's Port}}
Таким образом, ответ, возвращенный на порт X маршрутизатора, будет перенаправлен на устройство 1 (и соответствующий ему порт), а ответ, возвращенный на порт Y, будет перенаправлен на устройство 2.
Это называется NAT, и приведенное выше описание является очень простым примером.
«На каком уровне архитектуры HTTP мы получаем гарантию того, что ответ всегда будет возвращаться к его первоначальному запрашивающему? И как это реализовано?»
-- Это не имеет ничего общего с HTTP, это относится к уровню IP. Реализация происходит в роутере. Разработчику веб-приложений не нужно ничего делать.
@GershomMaes Да, вы можете получить «поддельный» сокет из объекта HTTP-запроса в Node.js. Пожалуйста, проверьте nodejs.org/dist/latest-v10.x/docs/api/…
MAC-адреса «уникально» идентифицируют устройства. Но даже если:
Прежде всего: http находится поверх tcp/ip. IP — это стандарт определения местоположения устройств в сети. Устройства получают IP-адрес от своего маршрутизатора, и устройство может отправлять маршрутизатору свой собственный IP-адрес. (192.168.2.1:80(маршрутизатор) -> 192.168.2.100:80(ПК))
Затем маршрутизатор отправляет данные в WWW через вашего провайдера.
Ваш общедоступный IP-адрес -Запрос> Интернет-провайдер -Запрос> Целевой IP-адрес -Ответ> Интернет-провайдер -Ответ> Вы
(Все это происходит, пока IP-адрес и порты пакета хранятся в пакете, который заключен в другие пакеты. Кроме того, в большинстве случаев существует гораздо больше переходов, чем эти.)
Затем есть порты: если вы используете порт, он будет локально заблокирован устройством. Если бы не было портов, у TCP/IP были бы те условия гонки, которые вы упомянули, поскольку они блокировали бы друг друга.
Дело в следующем: вы не используете разные порты или IP-адреса. Итак, что бы вы ни делали, это просто иметь один сервер, который обрабатывает оба входящих запроса, иначе второй сервер, который пытается заблокировать порт, не сможет этого сделать, поскольку он не соответствует протоколу TCP/IP. Вы не сможете запустить IP1:80/device1 и IP1:80/device2 с двумя разными приложениями, пытающимися заблокировать порт 80.
Так что вы в некотором роде правы (что будут расхождения), но также очень вводите в заблуждение мысль о том, что 2 сервера могут работать через порт 80.
Они могут справиться с делом, о котором вы говорите, следующим образом:
Либо
И для уточнения: HTTP это следующее:
Что серверы делают с HTTP, так это используют данные, которые они получают из формата HTTP. (это просто особый макет данных в TCP) И используйте это, чтобы определить, куда направить/направить результат.
Обработка сокетов, как упоминалось в shaochuancs, является частью TCP и UDP, двух протоколов низкого уровня. В сети существует около 7 уровней. Так что это только часть всей истории. Если вы хотите узнать больше, поищите Network Layers специально в Google. Это хорошее ключевое слово, чтобы получить больше информации об этом материале.
Вау, это отличное объяснение. Так что именно маршрутизатор выполняет работу по сокрытию истинных значений сокетов своих устройств! Он отправляет «поддельное» значение сокета в мир, а затем переназначает это значение при ответе, чтобы передать его нужному устройству. Из любопытства, есть ли способ в nodejs увидеть «поддельное» значение сокета? И я предполагаю, что мы говорим не только о моем домашнем маршрутизаторе, но и о переназначении, происходящем потенциально на каждом отдельном узле?