9.15. Імена та DNS¶
Усі попередні сторінки використовували числові IP-адреси – 192.168.1.20 та подібні. Реальні застосунки майже ніколи так не роблять. Сервери мають назви example.com або api.example.com, і застосунок шукає ім’я під час виконання, щоб знайти IP-адресу для надсилання пакетів. Цей пошук і є Системою доменних імен, або DNS.
9.15.1. Що повертає розв’язання імені¶
Ім’я — це просто мітка. example.com сам по собі не містить жодної IP-інформації – його потрібно шукати, так само як номер телефону шукають у телефонній книзі. Інфраструктура DNS є розподіленою телефонною книгою інтернету, і результатом пошуку є одна або декілька IP-адрес, до яких може підключитися камера.
example.com -> 93.184.216.34
Одне ім’я часто відповідає декільком адресам (для балансування навантаження, географічної надлишковості, версій IPv4 та IPv6 одного й того ж сервісу). Будь-яка з них підходить; застосунок вибирає одну і пробує її, переходячи до наступної, якщо вона не спрацьовує.
9.15.2. Як відбувається пошук¶
Коли камера запитує example.com:
Камера надсилає невелику UDP-датаграму (так, UDP – дивіться UDP – відправ пакет і сподівайся на краще) на свій налаштований DNS-сервер. Адреса DNS-сервера надійшла з того ж DHCP-обміну, який надав камері її власний IP.
DNS-сервер може вже мати відповідь у кеші (нещодавно його вже запитували). Якщо так – він відповідає негайно.
Якщо ні, DNS-сервер обходить глобальну ієрархію DNS: запитує кореневі сервери про
.com, запитує ці сервери проexample.com, запитує ті сервери про ім’я. Весь обхід дерева прихований від камери; камера бачить один запит і одну відповідь.DNS-сервер кешує результат для наступного разу та надсилає відповідь назад до камери у вигляді ще однієї UDP-датаграми.
Весь обмін зазвичай займає кілька мілісекунд при теплому кеші і до ста або більше при холодному.
9.15.3. Інтерфейс Python¶
Функція getaddrinfo() виконує пошук і повертає адресу, готову до передачі в конструктор сокету:
import socket
addr = socket.getaddrinfo("example.com", 80)[0][-1]
print(addr)
# ('93.184.216.34', 80)
Сигнатура: getaddrinfo(host, port). Значення, що повертається, – це список кортежів із 5 елементів (по одному на кожну розв’язану адресу); [0] вибирає перший, а [-1] – останнє поле, яким є кортеж (ip, port), що можна безпосередньо передати у сокет:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(socket.getaddrinfo("example.com", 80)[0][-1])
s.send(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
...
Більшість коду камери, що зв’язується з віддаленим сервером, починається з цього одного рядка. Допоміжні засоби asyncio (asyncio.open_connection()) виконують пошук внутрішньо, якщо передається ім’я замість числового IP, тому асинхронний код зазвичай не викликає getaddrinfo() безпосередньо.
9.15.4. Що може піти не так¶
DNS-сервер недоступний. Якщо Wi-Fi щойно піднявся і лінія нестабільна, перший виклик
getaddrinfo()може вийти за часом. ВиникаєOSError; повторіть спробу після стабілізації лінії.Ім’я не існує. Друкарська помилка або застаріле ім’я викликає
OSErrorпісля того, як DNS-сервер повертає «немає такого імені». Код помилки відрізняє це від «DNS-сервер недосяжний», але для більшості застосунків для камери політика повторних спроб/відмов однакова.Повернута адреса не працює. DNS може повернути адресу, яка більше не обслуговує цей сервіс. Вирішення – перейти до наступного запису у списку результатів
getaddrinfo()або виконати пошук імені пізніше (кеш DNS до того часу, швидше за все, оновиться).Захоплені портали. Деякі мережі Wi-Fi перехоплюють DNS і повертають IP-адресу сторінки захопленого порталу для всього. Камера, здається, підключається, але отримані дані не відповідатимуть тому, що надіслав би реальний сервіс. Не поширено в розгорнутих середовищах, але трапляється в конференційних мережах Wi-Fi та подібних.
9.15.5. Власне ім’я камери¶
Попередні сторінки виконували пошук імен інших пристроїв. У камери також є власне ім’я, яке вона оголошує в локальній мережі, коли запитує адресу. За замовчуванням це загальний ідентифікатор, що змінюється залежно від плати; network.hostname() замінює його на щось, що решта мережі впізнає:
import network
network.hostname("kitchen-cam")
# ... then bring the link up as usual ...
Встановіть ім’я хоста перед піднятям інтерфейсу, щоб ім’я передавалось як частина початкового запиту адреси камери, а не після.
Після приєднання камери до мережі відбуваються дві речі. По-перше, більшість домашніх маршрутизаторів реєструють імена хостів, яким вони надають адреси, у власному локальному довіднику, тому інші пристрої можуть звертатися до камери як kitchen-cam – не знаючи числової адреси, яку призначив маршрутизатор. (Корпоративні мережі можуть підтримувати або не підтримувати це; поведінка залежить від маршрутизатора.) По-друге, сама камера запускає резолвер mDNS з коробки, тому те саме ім’я також доступне як kitchen-cam.local у будь-якій мережі, клієнти якої розуміють mDNS – а це роблять більшість сучасних настільних операційних систем.
Примітка
Передавайте просте ім’я хоста у network.hostname() – просто "kitchen-cam", без суфіксу .local. Форма .local – це те, що mDNS додає під час пошуку; вбудовування його в ім’я хоста змушує камеру оголошувати kitchen-cam.local як просте ім’я хоста, що не є тим, чого очікує жодна зі сторін пошуку.
9.15.6. Коли імен недостатньо¶
Кілька ситуацій, в яких DNS не допомагає:
Виявлення в локальній мережі. Стандартний DNS відповідає на запитання про імена, зареєстровані в глобальному каталозі; він нічого не знає про пристрої в локальному сегменті. Multicast DNS (mDNS) – це система, яка заповнює цю прогалину. Кожен учасник приєднується до спеціальної групи multicast в локальній мережі та прослуховує запити; коли пристрій запитує ім’я, що закінчується на
.local, пристрій-власник цього імені відповідає безпосередньо. Без центрального сервера, без налаштування DNS. Bonjour на пристроях Apple, Avahi на Linux та Windows 10+ — всі говорять на одному протоколі – саме тому ім’яkitchen-cam.local, налаштоване в попередньому розділі, розв’язується в домашній мережі без жодних додаткових налаштувань.Сторона камери в цьому обміні є респондером, і він вже запущений. Чого у камери немає – це резолвера – іншої половини, яка дозволила б скрипту запитати мережу «де знаходиться
printer.local?» і отримати відповідь. Вбудований код mDNS є лише респондером (вбудовані пристрої зазвичай є знайденою річчю, а не тією, що шукає). Коли виявлення має відбуватися в зворотному напрямку, UDP broadcast (дивіться UDP – відправ пакет і сподівайся на краще) є простішою відповіддю для випадку локального сегмента, або чистий Python-модуль, такий як cbrand/micropython-mdns, додає повноцінний резолвер.Імена IPv6.
getaddrinfo()повертає результати як IPv4, так і IPv6, якщо обидва доступні. Вибирайте сімейство, яке може використовувати сокет застосунку.
Для більшості коду на стороні камери getaddrinfo – це один рядок на початку будь-якої функції, що відкриває мережеве з’єднання. Приклади в інших місцях цього розділу, що працювали з "192.168.1.20", працюватимуть так само з публічним іменем, як-от "api.example.com" – просто спочатку виконайте розв’язання.