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), які цей розділ не охоплює.