9.11. Об’єкти сокетів

Інтерфейс Python до транспортного рівня – це клас socket.socket. Сокет представляє один кінець мережевої розмови – адресу, порт і протокол (UDP або TCP), по якому ведеться розмова. У розділах з апаратного керування відкривалися екземпляри UART для спілкування по проводу; цей розділ відкриває екземпляри socket для спілкування по мережі. Форма та сама; базовий сервіс просто набагато потужніший.

9.11.1. Створення сокета

Три аргументи описують сокет: яке сімейство адрес він підтримує, який тип сокета пропонує та який протокол використовує. Значення за замовчуванням охоплюють випадки, що розглядаються в решті цього розділу:

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   # IPv4 TCP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)    # IPv4 UDP

Дві константи, між якими застосунок обирає майже в кожному випадку:

  • AF_INET – адреси IPv4 (числовий IP плюс порт). Найпоширеніший вибір. AF_INET6 – еквівалент для IPv6.

  • SOCK_STREAM – TCP-з’єднання, надійний потік байтів. SOCK_DGRAM – еквівалент для UDP.

Третій аргумент (протокол) залишається рівним нулю, що автоматично вибирає правильне значення за замовчуванням на основі перших двох. Повна документація конструктора знаходиться в socket — модуль сокетів.

9.11.2. Адреси сокета

Адреса сокета – це кортеж (host, port)

("192.168.1.50", 80)
("0.0.0.0", 8000)

Хост – це IP-адреса у вигляді рядка. Порт – це 16-бітне ціле число, розглянуте на сторінці Порти.

Варто знати кілька спеціальних рядків хоста:

  • "0.0.0.0" означає «кожен IPv4-інтерфейс на цьому пристрої». Сервер, прив’язаний до цієї адреси, приймає з’єднання на будь-якій адресі, яку має камера.

  • "127.0.0.1" – це localhost – трафік до нього ніколи не покидає пристрій. Корисно для тестування.

  • "255.255.255.255" – адреса локального широкомовного розсилання. UDP-датаграма, надіслана на неї, потрапляє на кожен пристрій у локальному сегменті.

Доменні імена типу "example.com" не є допустимими рядками хоста в адресі сокета. Їх спочатку потрібно перетворити на IP-адресу; сторінка Імена та DNS описує виклик getaddrinfo(), який це робить.

9.11.3. Дві ролі

Життєвий цикл сокета залежить від того, яку сторону розмови він займає. Клієнтський сокет викликає connect() (або, для UDP, просто sendto()) для спілкування з відомим сервером. Серверний сокет викликає bind() для отримання порту, потім listen() і accept() (для TCP) або recvfrom() (для UDP) для отримання вхідного трафіку.

В обох випадках використовується той самий конструктор socket; відрізняються лише методи, що викликаються після. Наступні три сторінки розповідають про практичні шаблони:

9.11.4. Закриття сокета

Кожен сокет займає невелику частину стану операційної системи (резервування порту, буфери, TCP-стан з’єднання). Коли застосунок закінчує роботу з ним, close() звільняє цей стан. Забутий сокет – це повільний витік ресурсів, який накопичується; в циклі, що відкриває з’єднання, відсутність close врешті-решт вичерпає пул доступних сокетів камери.

Найчистіший шаблон – оператор with

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(addr)
    s.send(b"GET / HTTP/1.0\r\n\r\n")
    ...
# socket is closed automatically here, even on error

Сокети реалізують протокол контекстного менеджера, розглянутий у загальному огляді Python, тому блок with гарантує виклик close() незалежно від того, чи блок завершився нормально, чи через виняток.

9.11.5. Довідник socket

Ця та наступні сторінки описують API у розповідній формі. Повний довідник на рівні аргументів для кожного методу, кожного прапора і кожної константи, що надається модулем, дивіться в socket — модуль сокетів. Довідник також є місцем для пошуку менш поширених операцій (параметри сокета, членство в групах multicast, ідентифікатори областей IPv6), які цей розділ не охоплює.