К старту курса по Fullstack-разработке на Python делимся заключительной частью руководства по программированию сокетов, эта часть посвящена устранению неполадок и справочным сведениям.


К концу руководства вы освоите основные функции и методы модуля Python socket, научитесь применять пользовательский класс для отправки сообщений и данных между конечными точками и работать со всем этим в собственных клиент-серверных приложениях.


Устранение проблем


Что-то будет «отказываться» работать, это неизбежно. И что тогда делать? Не переживайте, это случается с каждым. Надеемся, что с помощью этого руководства, отладчика и любимой поисковой системы вы сможете продолжить работу с частью исходного кода.


Иначе вам прямая дорога в модуль socket на Python. Обязательно прочитайте всю документацию по каждой вызываемой функции или методу. А чтобы почерпнуть какие-то идеи, загляните ещё в справочный раздел.


Иногда дело не в исходном коде. Он может быть правильным, а проблема связана с другим хостом, клиентом или сервером. Или сетью. А возможно, проводится «атака посредника», в качестве которого используется роутер, брандмауэр или какое-то другое сетевое устройство.


Для решения таких проблем нужны дополнительные инструменты. Ниже приводятся инструменты и утилиты, которые могут в этом пригодиться или хотя бы подвести к решению.


ping


Отправкой запроса проверки связи по ICMP в ping проверяется, работает ли хост и подключён ли он к сети. Взаимодействие ping со стеком протоколов TCP/IP прямое, поэтому его работа не зависит от каких бы то ни было приложений, запускаемых на хосте.


Вот пример запуска ping на macOS:


$ ping -c 3 127.0.0.1
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.058 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.165 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.164 ms

--- 127.0.0.1 ping statistics ---
3 packets transmitted, 3 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.058/0.129/0.165/0.050 ms

Статистика внизу может быть полезной, тчобы найти разрыв постоянного подключения и ответить на вопрос, теряются ли пакеты? Какова временнáя задержка? Можно посмотреть и время приёма-передачи.


Если между вами и другим хостом есть брандмауэр, запрос проверки связи в ping может быть запрещён. Политики соблюдения этого запрета реализуются администраторами брандмауэра, для которых обнаружение их хостов нежелательно. Если это ваш случай (и добавлены правила брандмауэра для взаимодействия хостов), убедитесь, что этими правилами также разрешается передача сообщений по ICMP между хостами.


ICMP — это протокол, применяемый в ping, а также протокол TCP и другие более низкоуровневые протоколы, используемые для передачи сообщений об ошибках. Причина странного поведения или медленных подключений может быть в ICMP.


ICMP-сообщения идентифицируются по типу и коду. Чтобы дать вам представление о важности их информации, приведём несколько таких сообщений:


Тип ICMP Код ICMP Описание
8 0 Запрос проверки связи
0 0 Ответ на запрос проверки связи
3 0 Сеть назначения недоступна
3 1 Хост назначения недоступен
3 2 Протокол назначения недоступен
3 3 Порт назначения недоступен
3 4 Требуется фрагментация, и установлен запрещающий флаг
11 0 Превышен срок жизни (TTL) при передаче пакета

Информацию о фрагментации и ICMP-сообщениях см. в статье об обнаружении наименьшего MTU на пути следования пакета в сети. Это пример того, чтó может быть причиной странного поведения.


netstat


В разделе «Просмотр состояния сокета» вы узнали, как можно использовать netstat для отображения информации о сокетах и их текущем состоянии. Эта утилита доступна на macOS, Linux и Windows.


В том разделе столбцы Recv-Q и Send-Q в выводе примера отсутствовали. В них показано количество байтов, которые хранятся в сетевых буферах и поставлены в очередь на передачу или приём, но по какой-то причине не считаны или не записаны в удалённом или локальном приложении.


То есть они остаются в ожидании в сетевых буферах, очередях ОС. Одна из причин — в приложении ограничено использование ресурсов ЦП либо нельзя вызвать socket.recv() или socket.send() и обработать байты. Или это проблемы с сетью, которые отражаются на передаче данных, например перегрузка сети или неисправное сетевое оборудование / кабели.


Посмотрим, сколько данных вы сможете отправить, прежде чем увидите ошибку. Попробуйте тестовый клиент с подключением к тестовому серверу и многократным вызовом socket.send(). В тестовом сервере никогда не вызывается socket.recv(), а лишь принимается подключение. Это становится причиной заполнения сетевых буферов на сервере, после чего в клиенте выдаётся ошибка.


Сначала запустите сервер:


$ python app-server-test.py 127.0.0.1 65432
Listening on ('127.0.0.1', 65432)

Затем, чтобы увидеть ошибку, запустите клиент:


$ python app-client-test.py 127.0.0.1 65432 binary test
Error: socket.send() blocking io exception for ('127.0.0.1', 65432):
BlockingIOError(35, 'Resource temporarily unavailable')

Вот вывод netstat, когда в клиенте и на сервере продолжается выполнение. При этом в клиенте многократно выводится приведённое выше сообщение об ошибке:


$ netstat -an | grep 65432
Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp4  408300      0  127.0.0.1.65432        127.0.0.1.53225        ESTABLISHED
tcp4       0 269868  127.0.0.1.53225        127.0.0.1.65432        ESTABLISHED
tcp4       0      0  127.0.0.1.65432        *.*                    LISTEN

Первая запись — это сервер (у Local Address порт 65432):


Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp4  408300      0  127.0.0.1.65432        127.0.0.1.53225        ESTABLISHED

Обратите внимание на 408300 в Recv-Q.


Вторая запись — это клиент (у Foreign Address порт 65432):


Proto Recv-Q Send-Q  Local Address          Foreign Address        (state)
tcp4       0 269868  127.0.0.1.53225        127.0.0.1.65432        ESTABLISHED

Обратите внимание на 269868 в Send-Q.


В клиенте, конечно, пытались записать байты, но на сервере они не считывались. Это стало причиной заполнения очереди сетевого буфера как на стороне приёма на сервере, так и на стороне отправки в клиенте.


Windows


Если работаете с Windows, обязательно ознакомьтесь с набором утилит Windows Sysinternals.


Одна из них — TCPView.exe. Это графический netstat для Windows. Кроме адресов, номеров портов и состояния сокета, в ней нарастающим итогом показывается число отправленных и полученных пакетов и байтов. Как и в случае с утилитой lsof в Unix, здесь вы получаете имя и идентификатор процесса. Другие параметры отображения см. в меню.


Снимок экрана TCPView


Wireshark


Иногда нужно увидеть, что происходит при передаче по сети. Забудьте, чтó там в журнале приложения или какое значение возвращается из библиотечного вызова. Вам нужно видеть, чтó отправляется или получается в сети на самом деле. Здесь как с отладчиками: когда нужно увидеть это, они незаменимы.


Wireshark — это анализатор сетевых протоколов и приложение для захвата трафика, запускаемое на macOS, Linux, Windows и других ОС. Существует две версии: wireshark с графическим интерфейсом и текстовая tshark для терминала.


Захват трафика — это отличный способ понаблюдать за поведением приложения в сети и узнать: чтó, как часто и сколько в нём отправляется и получается. А ещё увидеть, когда клиент или сервер закрывается, когда в них прерывается подключение или прекращается отправка ответов. Эта информация может очень пригодиться при устранении проблем.


В интернете много хороших руководств и других ресурсов по основам использования Wireshark и TShark.


Вот пример захвата трафика в интерфейсе «внутренней петли» с помощью Wireshark:


Снимок экрана Wireshark


А вот тот же пример с tshark:


$ tshark -i lo0 'tcp port 65432'
Capturing on 'Loopback'
    1   0.000000    127.0.0.1 → 127.0.0.1    TCP 68 53942 → 65432 [SYN] Seq=0 Win=65535 Len=0 MSS=16344 WS=32 TSval=940533635 TSecr=0 SACK_PERM=1
    2   0.000057    127.0.0.1 → 127.0.0.1    TCP 68 65432 → 53942 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=16344 WS=32 TSval=940533635 TSecr=940533635 SACK_PERM=1
    3   0.000068    127.0.0.1 → 127.0.0.1    TCP 56 53942 → 65432 [ACK] Seq=1 Ack=1 Win=408288 Len=0 TSval=940533635 TSecr=940533635
    4   0.000075    127.0.0.1 → 127.0.0.1    TCP 56 [TCP Window Update] 65432 → 53942 [ACK] Seq=1 Ack=1 Win=408288 Len=0 TSval=940533635 TSecr=940533635
    5   0.000216    127.0.0.1 → 127.0.0.1    TCP 202 53942 → 65432 [PSH, ACK] Seq=1 Ack=1 Win=408288 Len=146 TSval=940533635 TSecr=940533635
    6   0.000234    127.0.0.1 → 127.0.0.1    TCP 56 65432 → 53942 [ACK] Seq=1 Ack=147 Win=408128 Len=0 TSval=940533635 TSecr=940533635
    7   0.000627    127.0.0.1 → 127.0.0.1    TCP 204 65432 → 53942 [PSH, ACK] Seq=1 Ack=147 Win=408128 Len=148 TSval=940533635 TSecr=940533635
    8   0.000649    127.0.0.1 → 127.0.0.1    TCP 56 53942 → 65432 [ACK] Seq=147 Ack=149 Win=408128 Len=0 TSval=940533635 TSecr=940533635
    9   0.000668    127.0.0.1 → 127.0.0.1    TCP 56 65432 → 53942 [FIN, ACK] Seq=149 Ack=147 Win=408128 Len=0 TSval=940533635 TSecr=940533635
   10   0.000682    127.0.0.1 → 127.0.0.1    TCP 56 53942 → 65432 [ACK] Seq=147 Ack=150 Win=408128 Len=0 TSval=940533635 TSecr=940533635
   11   0.000687    127.0.0.1 → 127.0.0.1    TCP 56 [TCP Dup ACK 6#1] 65432 → 53942 [ACK] Seq=150 Ack=147 Win=408128 Len=0 TSval=940533635 TSecr=940533635
   12   0.000848    127.0.0.1 → 127.0.0.1    TCP 56 53942 → 65432 [FIN, ACK] Seq=147 Ack=150 Win=408128 Len=0 TSval=940533635 TSecr=940533635
   13   0.001004    127.0.0.1 → 127.0.0.1    TCP 56 65432 → 53942 [ACK] Seq=150 Ack=148 Win=408128 Len=0 TSval=940533635 TSecr=940533635
^C13 packets captured

Далее приводим вам в помощь справочные материалы по программированию сокетов.


Справочный раздел


Этот раздел может использоваться в качестве справочника общего содержания с дополнительной информацией и ссылками на внешние ресурсы.


Документация Python



Ошибки


Следующее взято из документации к модулю socket на Python:


«Все ошибки сопровождаются вызовом исключений. При недопустимых типах аргументов и нехватке памяти могут вызываться обычные исключения. Начиная с Python 3.3 ошибки, связанные с семантикой сокетов или адресов, сопровождаются вызовом OSError или одного из его подклассов». (Источник).

Вот типичные ошибки, с которыми вы можете столкнуться при работе с сокетами:


Исключение Константа errno Описание
BlockingIOError EWOULDBLOCK Ресурс временно недоступен. Например, в неблокируемом режиме, когда вызывается .send() и одноранговый узел занят (но не считыванием), очередь на отправку (сетевой буфер) заполнена. Или имеются проблемы с сетью. Надеемся, что это временное состояние.
OSError EADDRINUSE Адрес уже используется. Убедитесь, что не запущен другой процесс с тем же номером порта и на сервере задаётся параметр сокета SO_REUSEADDR: socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1).
ConnectionResetError ECONNRESET Сброс подключения на одноранговом узле. Удалённый процесс аварийно завершён или его сокет закрыт некорректно (это ещё называют неправильным завершением работы). Или в сетевом пути располагается брандмауэр либо другое устройство, у которого отсутствуют правила или отмечается неправильное поведение.
TimeoutError ETIMEDOUT Превышено время ожидания операции. Нет отклика от однорангового узла.
ConnectionRefusedError ECONNREFUSED В подключении отказано. Указанный порт никаким приложением не прослушивается.

Семейства адресов сокетов


socket.AF_INET и socket.AF_INET6 — это семейства адресов и протоколов для первого аргумента в socket.socket(). В API предполагается адрес определённого формата — в зависимости от того, создан сокет с помощью socket.AF_INET или socket.AF_INET6.


Семейство адресов Протокол Кортеж с адресом Описание
socket.AF_INET IPv4 (host, port) host — это строка с именем хоста, например 'www.example.com', или IPv4-адрес вроде '10.1.2.3'. port — целое число.
socket.AF_INET6 IPv6 (host, port, flowinfo, scopeid) host — это строка с именем хоста, например 'www.example.com', или IPv6-адрес вроде 'fe80::6203:7ab:fe88:9c23'. port — целое число. flowinfo и scopeid — это элементы sin6_flowinfo и sin6_scope_id в структуре sockaddr_in6 на C.

Ниже приведена выдержка из документации Python к модулю socket о значении host в кортеже адресов:


«Для IPv4-адресов вместо адреса хоста принимаются две специальные формы: пустая строка — это INADDR_ANY, а строка '<broadcast>' — это INADDR_BROADCAST. Такое поведение несовместимо с IPv6, так что они вам, возможно, не понадобятся, если вы намерены поддерживать IPv6 в своих программах на Python». (Источник)

Подробнее см. в документации по семействам сокетов на Python.


В этом руководстве применяются сокеты IPv4. Если в вашей сети поддерживается IPv6, попробуйте по возможности протестировать и использовать эту версию. Это легко делается с функцией socket.getaddrinfo(). Её аргументы host и port преобразуются в последовательность из пяти кортежей со всеми аргументами, необходимыми для создания сокета, подключённого к этому сервису. В socket.getaddrinfo() принимаются и интерпретируются переданные IPv6-адреса и имена хостов, которые разрешаются не только в IPv4, но и в IPv6-адреса.


В следующем примере возвращается информация об адресе для TCP-подключения к example.org в порте 80:


>>> socket.getaddrinfo("example.org", 80, proto=socket.IPPROTO_TCP)
[(<AddressFamily.AF_INET6: 10>, <SocketType.SOCK_STREAM: 1>,
 6, '', ('2606:2800:220:1:248:1893:25c8:1946', 80, 0, 0)),
 (<AddressFamily.AF_INET: 2>, <SocketType.SOCK_STREAM: 1>,
 6, '', ('93.184.216.34', 80))]

На вашем компьютере результаты могут отличаться, если IPv6 не включена. Возвращаемые выше значения можно передавать в socket.socket() и socket.connect(). В документации Python к модулю socket, в разделе с примерами, содержится пример клиента и сервера.


Использование имён хостов


Кстати, этот раздел примени́м в основном к использованию имён хостов с .bind() и .connect() или .connect_ex(), когда задействуется интерфейс «внутренней петли» (localhost). Но примени́м он и в любой момент, когда вы используете имя хоста и ожидается его разрешение в определённый адрес. Причём имя хоста имеет особое значение для приложения, что отражается на его поведении или делаемых в нём допущениях. Это отличается от типичного сценария, когда в клиенте имя хоста используется для подключения к серверу, разрешённому при помощи DNS, например www.example.com.


Следующее взято из документации к модулю socket на Python:


«Если использовать имя хоста в хостовой части адреса сокета IPv4/v6, в программе может проявиться недетерминированное поведение, поскольку в Python используется первый адрес, возвращаемый из DNS-разрешения. Адрес сокета будет разрешён в фактический адрес IPv4/v6 по-разному в зависимости от результатов из DNS-разрешения и/или конфигурации хоста. Для детерминированного поведения используйте в хостовой части числовой адрес». (Источник).

Стандартным именем localhost предусматривается его разрешение в 127.0.0.1 или ::1, интерфейс «внутренней петли». В вашей системе наверняка так и будет. А может, и нет. Это зависит от того, как она сконфигурирована для разрешения имён. Как и во всём, что связано с ИТ, всегда имеются исключения, и нет никаких гарантий, что при использовании имени localhost произойдёт подключение к интерфейсу «внутренней петли».


Например, на Linux см. файл конфигурации man nsswitch.conf диспетчера службы имён. На macOS и Linux стóит ещё заглянуть в файл /etc/hosts. На Windows см. C:\Windows\System32\drivers\etc\hosts. В файле hosts имеется статическая таблица сопоставления имён и адресов в простом текстовом формате. DNS — это совершенно иная часть пазла.


Примечательно, что с июня 2018 года существует черновой вариант RFC Let ‘localhost’ be localhost («Пусть ‘localhost’ будет localhost»), где обсуждаются соглашения, допущения и вопросы безопасности, связанные с применением имени localhost.


Важно понимать: когда в приложении используются имена хостов, в качестве адресов может возвращаться буквально всё что угодно. Не делайте допущений относительно имени, если в приложении важна безопасность. Представляет это повод для беспокойства или нет — зависит от приложения и окружения.


Даже если в приложении важность безопасности неочевидна, соответствующие рекомендации всё равно применяются. Если в приложении имеется доступ к сети, оно должно сопровождаться и быть защищено. То есть это как минимум:

  • Регулярные обновления системного ПО и исправления уязвимостей, в том числе на Python. Обязательно также проверяйте и обновляйте любые сторонние библиотеки.
  • Чтобы подключаться только к доверенным системам, по возможности используйте выделенный брандмауэр или межсетевой экран узлов.
  • Какие DNS-серверы сконфигурированы? Вы доверяете им и их администраторам?
  • Убедитесь, что данные запроса максимально очищены и проверены, прежде чем вызывать другой код, в котором они обрабатываются. Используйте для этого фаззинг-тесты и запускайте их регулярно.

Независимо от того, используете вы имена хостов или нет, если в приложении нужна поддержка безопасных подключений через шифрование и аутентификацию, то вам, вероятно, стóит попробовать TLS. Но это — тема отдельной статьи и в настоящем руководстве не рассматривается. Чтобы начать с ним работу, см. документацию по модулю ssl на Python. Это тот же протокол, который применяется в браузерах для безопасного подключения к сайтам.


Интерфейсы, IP-адреса, разрешения имён… Переменных много. Что же делать? Если у вас нет процесса рассмотрения сетевых приложений, используйте эти рекомендации:


Приложение Применение Рекомендация
Сервер Интерфейс «внутренней петли» Используйте IP-адрес, например 127.0.0.1 или ::1.
Сервер Интерфейс ethernet Используйте IP-адрес, например 10.1.2.3. Чтобы поддерживать более одного интерфейса, оставляйте во всех интерфейсах/адресах пустую строку. См. выше примечание по безопасности.
Клиент Интерфейс «внутренней петли» Используйте IP-адрес, например 127.0.0.1 или ::1.
Клиент Интерфейс ethernet Используйте IP-адрес для обеспечения согласованности и независимости от разрешения имён. Для типичного случая используйте имя хоста. См. выше примечание по безопасности.

Для клиентов или серверов, если нужно аутентифицировать хост, к которому подключаетесь, стóит попробовать TLS.


Блокирующие вызовы


Блокирующий вызов — это функция или метод сокета, на которых работа приложения временно приостанавливается. Например, .accept(), .connect(), .send() и .recv() блокируются, то есть немедленного возвращения в них не происходит. Прежде чем от блокирующих вызовов возвращается значение, они находятся в ожидании завершения системных (ввод-вывод). Поэтому источник вызова блокируется до их завершения, или истечения тайм-аута, или возникновения другой ошибки.


Чтобы в блокирующих вызовах сокетов происходило немедленное возвращение значений, такие вызовы можно настроить на неблокируемый режим. При этом, чтобы обрабатывать готовую операцию сокета, нужно как минимум провести рефакторинг или перепроектирование приложения.


Поскольку в вызове значения возвращаются немедленно, данные могут быть не готовы. Вызываемая сторона находится в сети в ожидании — ей не хватило времени завершить свою работу. В этом случае текущий статус — это errno-значение socket.EWOULDBLOCK. Неблокируемый режим поддерживается с помощью .setblocking().


По умолчанию сокеты всегда создаются в блокируемом режиме. Описание трёх режимов см. в примечаниях по тайм-аутам сокетов.


Закрытие подключений


В TCP примечательно то, что в клиенте или на сервере совершенно допустимо закрывать свою сторону подключения, пока другая остаётся открытой. Это называется «полуоткрытым» подключением. Желательно оно или нет — решается в приложении. Вообще говоря, нежелательно. В этом состоянии со стороны, чей конец подключения закрыт, данные больше отправляться не смогут. Они будут только приниматься.


Этот подход рекомендуется не как обязательный, но в HTTP в качестве примера используется заголовок с именем Connection для стандартизации того, как в приложениях должны закрываться или сохраняться открытыми подключения. Подробнее см. в разделе 6.3 RFC 7230 о протоколе передачи гипертекста (HTTP/1.1), синтаксисе и маршрутизации сообщений.


При разработке и написании приложения и его протокола прикладного уровня рекомендуется заранее продумать закрытие подключений. Иногда это просто и очевидно, а иногда могут потребоваться создание прототипов и предварительное тестирование. Всё зависит от приложения и от того, как обрабатывается цикл сообщений с ожидаемыми данными. Просто убедитесь, что сокеты всегда своевременно закрываются после завершения работы.


Порядок следования байтов


Подробнее о том, как порядок байтов хранится в памяти разных ЦП, см. в соответствующей статье «Википедии». При интерпретации отдельных байтов это не проблема. Но при обработке нескольких байтов, считываемых и обрабатываемых как одно значение, например 4-байтовое целое число, порядок байтов нужно изменить на противоположный, если взаимодействовать с компьютером, на котором другой порядок следования байтов.


Порядок байтов важен и для текстовых строк, представленных в виде многобайтовых последовательностей, таких как «Юникод». Если только вы не используете всегда true, строгий ASCII и не контролируете реализацию клиента и сервера, вам наверняка лучше использовать «Юникод» с кодировкой вроде UTF-8 или той, которой поддерживается маркер последовательности байтов.


Важно явно определить кодировку, применяемую в протоколе прикладного уровня. Это можно сделать, установив в качестве обязательной для всего текста UTF-8 или использовав заголовок content-encoding, в котором эта кодировка указана. Так приложение избавляется от необходимости определять кодировку, которую по возможности следует избегать.


Это становится проблематичным, когда имеются данные, которые хранятся в файлах или базе данных, и недоступны метаданные, в которых указана их кодировка. Когда данные передаются на другую конечную точку, в ней придётся попробовать определить кодировку. Идеи для обсуждения см. в статье «Википедии» о «Юникоде», в которой упоминается о RFC 3629: UTF-8, формат преобразования ISO 10646:


«Однако в RFC 3629 (стандарт UTF-8) рекомендуется запрещать маркер последовательности байтов в протоколах с UTF-8, но обсуждаются случаи, когда это бывает невозможно. Кроме того, под большим ограничением на возможные шаблоны в UTF-8 (например, не может быть одиночных байтов с установленным старшим битом) подразумевается, что должна быть возможность отличать UTF-8 от других кодировок символов без применения маркера последовательности байтов». (Источник).

А вывод из этого такой: всегда сохранять кодировку, используемую для обрабатываемых в приложении данных, если она может меняться. То есть пытаться каким-то образом сохранять кодировку в виде метаданных, если это не всегда UTF-8 или какая-то другая кодировка с маркером последовательности байтов. Затем можно отправить её в заголовке вместе с данными, чтобы сообщить получателю, что это такое.


В TCP/IP применяется обратный порядок байтов, который называется сетевым. Сетевой порядок используется для представления целых чисел на нижних уровнях стека протоколов, таких как IP-адреса и номера портов. В модуль socket на Python включены функции, в которых целые числа преобразуются в порядок байтов сети и хоста и обратно:


Функция Описание
socket.ntohl(x) 32-битные положительные целые числа преобразуются из порядка байтов сети в порядок байтов хоста. На компьютерах, где порядок байтов хоста и сети одинаков, эта операция не выполняется. Если неодинаков, перестанавливается четыре байта.
socket.ntohs(x) 16-битные положительные целые числа преобразуются из порядка байтов сети в порядок байтов хоста. На компьютерах, где порядок байтов хоста и сети одинаков, эта операция не выполняется. Если неодинаков, перестанавливается два байта.
socket.htonl(x) 32-битные положительные целые числа преобразуются из порядка байтов хоста в порядок байтов сети. На компьютерах, где порядок байтов хоста и сети одинаков, эта операция не выполняется. Если неодинаков, перестанавливается четыре байта.
socket.htons(x) 16-битные положительные целые числа преобразуются из порядка байтов хоста в порядок байтов сети. На компьютерах, где порядок байтов хоста и сети одинаков, эта операция не выполняется. Если неодинаков, перестанавливается два байта.

Кроме того, чтобы с помощью строк формата упаковывать и распаковывать двоичные данные, можно использовать модуль struct:


import struct
network_byteorder_int = struct.pack('>H', 256)
python_int = struct.unpack('>H', network_byteorder_int)[0]

Заключение


В этом руководстве рассмотрено много вопросов! Сети и сокеты — это большие темы. Если они вам в новинку, пусть вас не смущают все эти термины и сокращения.


Чтобы понять, как здесь всё сочетается, нужно ознакомиться с кучей подробностей. Но в Python начинаешь лучше понимать целое, осваивая отдельные части и проводя с ними больше времени. Теперь вы можете задействовать свой пользовательский класс и, опираясь на него, проще и быстрее создавать собственные приложения с сокетами.


Ознакомиться с примерами можно по ссылке ниже:


исходный код из руководства для примеров в этом руководстве.

Поздравляем с тем, что добрались до конца! Вы уже готовы применять сокеты в собственных приложениях. Желаю вам успехов в разработке сокетов.


Научим вас аккуратно работать с данными, чтобы вы прокачали карьеру и стали востребованным IT-специалистом. Если вы не найдёте работу, мы просто вернём деньги (возврат — акция в рамках «Чёрной пятницы»).




Комментарии (1)


  1. Paskin
    24.11.2022 09:55
    -1

    Вы уж извините за прямоту - но это скорее поток сознания, чем руководство.
    Если это "устранение проблем" - то его пишут по принципу проблема-причина-как определить-как устранить. Если справочник - то его делят на разделы и не растекаются мыслью по древу.