Как стать автором
Поиск
Написать публикацию
Обновить

Книга: «Сетевое программирование. От основ до приложений»

Время на прочтение16 мин
Количество просмотров9.1K
Привет, Хаброжители!

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

Книга «Сетевое программирование. От основ до приложений» предлагает читателям не просто поверхностный обзор, а глубокое и систематизированное руководство, охватывающее все ключевые аспекты разработки сетевых решений.

Об авторах:

Артем Нечипорук окончил Московский государственный университет экономики, статистики и информатики по специальности «Прикладная информатика в экономике».

В настоящее время — архитектор программного обеспечения в отделе перспективных исследований «Лаборатории Касперского». Один из его проектов — «Kaspersky Neuromorphic Platform», платформа с открытым кодом для выполнения импульсных нейронных сетей.

Алексей Баринов — разработчик, свыше 25 лет проработавший в ИТ, более половины из них в области сетевой безопасности.

В настоящее время — разработчик-аналитик в компании RDP.RU, специализирующейся на создании решений для защиты сетевого трафика.
Об авторах и экспертах
Книга не появилась бы на свет без экспертизы множества людей, причем многие из них являются непосредственными разработчиками инструментов, описанных в книге.

Эксперты


Алексей Кузнецов в конце 1990-х и начале 2000-х годов являлся одним из ключевых разработчиков ядра Linux, занимая позицию мейнтейнера сетевой подсистемы с 2000 по 2003 год. За этот период Алексей существенно усовершенствовал сетевой стек Linux, адаптировав его для многоядерных систем, улучшив поддержку IPv6 и внедрив средства управления трафиком.

В дополнение к этому он переработал поддержку протокола TCP, что привело к созданию нового TCP-стека, который был представлен в версии ядра Linux 2.2 и до сих пор широко востребован. Среди разработанных Алексеем инструментов наиболее известны утилиты из набора iputils: ping, tracepath, tftpd, rarpd и утилиты для управления трафиком iproute2: ip, tc, ss.

Стивен Хеммингер (Stephen Hemminger) начал работать над TCP/IP и сетевыми драйверами для Unix с Tektronix, Sequent и nCube, а затем унаследовал разработку сети в Linux от Алексея Кузнецова. Более 20 лет в Linux Foundation он занимался ядром Linux, iproute2 и мостами. Оттуда он перешел в Vyatta, где работал главным инженером проекта программного маршрутизатора на базе Linux. Затем, чтобы внести свой вклад в сетевые драйверы Linux для Azure, он перешел на позицию архитектора в Microsoft.

В настоящее время он совместно с Дэвидом Ахерном занимается сопровождением iproute2.

Петр Савельев — автор пакета PyRoute2 и специалист по сокетам Netlink. Обладает более чем 20-летним опытом в индустрии телекома. Во время работы в Ericsson принимал участие в разработке сетевой подсистемы OpenStack, затем работал с сетями в Amazon Global Payments. На данный момент занимается решениями и аудитом сетевой безопасности в Kubernetes.

Владимир Строгов — эксперт по ядру Windows. Имеет свыше 30 лет опыта разработки на уровне ядра в области хранения данных и безопасности. Занимался файловыми системами, виртуализацией, защитой данных и антивирусной защитой, обратным инжинирингом.
Почти 10 лет Владимир работал над проектами Veritas и Symantec. Затем трудился в «Лаборатории Касперского», в группе Core Drivers, на позициях технического эксперта и руководителя группы. Сейчас Владимир является директором по развитию команды ядра в международной компании.

Никита Соболев — core-разработчик языка Python и его основного интерпретатора — CPython. На постоянной основе занимается разработкой различных open-source-технологий. Проводил ревью кода Python в книге.

Сергей Талантов — старший архитектор программного обеспечения в группе разработки шлюзов для интернета вещей на базе ОС ЛК. Проводил ревью кода C++. Контрибьютор в браузер Chromium.

Александр Певзнер — эксперт в области технологий сети, протоколов и сетевого оборудования. Проводил консультации по топологии сетей, работе сетевого оборудования, сетевым протоколам и их применению в современной практике. Старший системный инженер группы по управлению телекоммуникациями в «Лаборатории Касперского». Имеет большой опыт работы с сетями и автоматизацией управления сетевой инфраструктурой.

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

Консультанты


Перечисленные ниже консультанты оказывали помощь в решении некоторых технических вопросов, относящихся к узким областям и конкретным продуктам.

Руслан Морозов — руководитель отдела разработки решений для рабочих станций и мобильных устройств на базе ОС ЛК. Руслан обладает экспертизой во многих областях, рассмотренных в книге, в особенности в области отладки, безопасности и ядра Linux, а также в сложных вопросах, которые выходят за рамки первой книги.

Александр Гетьман — кандидат физико-математических наук. Занимается направлением анализа сетевого трафика в Институте системного программирования РАН. Александр консультировал авторов по сложным вопросам: например, проксирование, VPN и др. В книге использованы материалы некоторых его статей.

Андрей Федотов — кандидат технических наук, ранее старший научный сотрудник ИСП РАН, работает в компании Яндекс. Андрей предоставил консультации по работе систем статического анализа Svace и системы фаззинг-тестирования Sydr, членом команды разработки которых он являлся.

Святослав Размыслов — продуктовый менеджер PVS-Studio LLC. Он поделился знаниями о работе и возможностях статического анализатора кода PVS-Studio и помог составить описание анализатора PVS-Studio.

Ревьюеры


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

Сергей Талантов проверил и исправил код примеров на C++ — его коммиты доступны в репозитории книги.

Никита Соболев проверил и скорректировал весь Python-код из книги.

Андрей Маркобородов — старший разработчик «Лаборатории Касперского» в группе разработки решений для защиты сетей. Вычитал текст, диаграммы и код глав 3–12, 23, 24 и внес свои конструктивные замечания.

Владислав Егоров — специалист по направлению анализа сетевого трафика в ИСП РАН. Выполнил ревью глав 23 и 24.

Максим Дмитриченко — системный программист с более чем 20-летним опытом разработки. Пишет на C, C++ и Rust. Занимается разработкой встраиваемых систем, ЦОС, высокоскоростных систем анализа информации и принятия решений, обратной инженерией сетевых протоколов. Выполнил ревью глав 20–22, внес замечания по работе прокси. Провел ревью диаграммы сетевого стека Linux в главе 3, а также исследование по главе 6, выяснив, как избежать гонки при обработке сигнала.

Петр Савельев провел вычитку и исправления глав 11 и 12 по теме Netlink.

Александр Певзнер проверил все описания работы протоколов и особенности работы сети в главах 1–6 и 10–24.

Александр Гетьман провел ревью глав 21–23.

Владимир Строгов проверил диаграммы функционирования сетевого стека Windows в главе 3 и проверил главы 14–20 по ОС Windows.

Алексей Кузнецов проверил главу 8, по теме опций управления сокетами.

Руслан Морозов провел ревью глав 7–12.

Андрей Грес — инженер-разработчик встраиваемых систем на основе Linux, занимается конструированием и разработкой программного обеспечения для автомобильных систем экстренного реагирования при авариях и ДТП. Выполнил ревью всех глав, помог с написанием книги, доработал глоссарий, а также оказал помощь с проверкой кода примеров и исправлением примера с OOB.

Александр Цыганкин — технический руководитель кластера «Кибербезопасность» в компании MTS Digital, занимается созданием решений в сфере кибербезопасности и защиты инфраструктуры с использованием искусственного интеллекта. Провел ревью глав с 1-й по 4-ю. Исправил некоторые серьезные ошибки, в том числе в коде и терминологии.

Генрих Селеня — аспирант ОИПИ. Генрих пишет серверы на C++. Провел ревью глав 7 и 8.

Владимир Протасов — технический менеджер компании Яндекс. Провел ревью введения, а также глав 1 и 2.

Павел Балаев — старший разработчик Tarantool, ранее исследователь в отделе технологических исследований компании «Инфотекс». Провел ревью глав 1 и 2.

Прочие участники


Автор всех иллюстраций в книге — Ксения Баринова.

История Udev была написана с помощью участников русского сообщества Debian:
Алексей Петров — к. ф.-м. н., ФТИ им. Иоффе РАН.

Максим Дмитриченко — системный программист.

Илья Казакевич — участник русского сообщества Debian.

Авторы Артем Нечипорук и Алексей Баринов объединили многолетний опыт работы в области сетевых технологий и информационной безопасности, а также привлекли к созданию книги экспертов — разработчиков сетевых стеков и операционных систем. В результате получился материал, сочетающий фундаментальную теорию с проверенными на практике примерами на C++, Python и Go.

Почему эта книга уникальна?


Многие книги по сетевому программированию фокусируются либо на базовых сокетах в C, либо на узкоспециализированных библиотеках, быстро устаревающих из-за развития технологий. Новички часто ошибочно полагают, что создание сетевых приложений сводится к простым операциям: открыл соединение, передал данные, закрыл. Однако реальная разработка сталкивается со множеством сложностей — от управления параллельными процессами до обеспечения безопасности и масштабируемости.

Эта книга стремится заполнить пробел, предлагая не только практические примеры, но и глубокое понимание принципов, лежащих в основе сетевых технологий. Но так как объем рассмотрения всех затронутых тем очень большой, пришлось материал разбить на три книги:
  1. «Сетевое программирование. От основ до приложений» (книга уже вышла).
  2. «Сетевое программирование. Практика разработки приложений» (готовится к выходу).
  3. «Сетевое программирование. Безопасность и масштабируемые системы» (готовится к выходу).

Основой для материала послужили лекции, которые один из авторов разработал для онлайн-университета и дополнил по запросам студентов. Однако книга — не просто переработанный курс, а значительно расширенное и дополненное издание, включающее редкие аспекты, которые редко освещаются в других источниках.

Что внутри?


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

Последующие книги охватывают более сложные темы, такие как создание сетевых API, работа с разнообразными библиотеками.

Основные языки в книге — C++ и Python. Хотя C++ выбран в качестве основного, авторы рекомендуют изучать примеры даже тем, кто предпочитает Python, поскольку это помогает лучше понять внутренние механизмы. Для закрепления материала в конце каждой главы приведены вопросы и упражнения.

Для кого эта книга?


Книга рассчитана на разработчиков с базовым опытом, которые хотят углубить свои знания в сетевом программировании. Читателю потребуется понимание основ TCP/IP, DNS и навыки программирования на C++ (выше начального уровня) или Python. Тем, кто уже знаком с сокетами по другим источникам (например, по книге «UNIX: разработка сетевых приложений» У. Стивенса или Beej’s Guide to Network Programming), можно пропустить начальные разделы.

Эта книга — итог многолетней работы профессионалов, готовых поделиться своим опытом. Она станет надежным проводником в мире сетевого программирования, помогая не только освоить теорию, но и применять ее в реальных проектах.

Предлагаем ознакомиться с отрывком «Простой обмен данными. RAW-СОКЕТЫ»

Простой обмен данными. RAW-СОКЕТЫ



Введение

В этой главе мы научимся обмениваться данными через сокеты без соединения на примере UDP. Этот протокол имеет определенные преимущества по скорости передачи данных перед TCP, но у него есть и недостатки, такие как отсутствие гарантий доставки пакетов и сохранения порядка их следования.

Тем не менее он незаменим для таких задач, как трансляция видео или аудио в режиме реального времени или других приложений, требующих высокой скорости передачи за счет возможной потери части данных.
Мы, наконец, рассмотрим API отправки и приема данных. И используя его, реализуем «клиент» и «сервер» для обмена без установки соединения.

Затем мы изучим raw-сокеты, которые позволяют отправлять и получать заголовки протоколов, сформированные на прикладном уровне. Используя их, мы реализуем взаимодействие по протоколу ICMP, который является частью стека TCP/IP и служит для передачи служебных и диагностических сообщений между узлами в сети, что играет критически важную роль в обслуживании интернет-коммуникаций, позволяя отслеживать состояние инфраструктуры.

Мы реализуем наиболее распространенный вариант использования ICMP — утилиту Ping для проверки доступности узлов в сети. И посмотрим, как на примере данной утилиты raw-сокеты используются в разных языках, в частности Python и Go.

Выше мы уже видели пример сокетов, ориентированных на сообщения. Они проще в использовании, чем потоковые, так как не требуют самостоятельной сборки данных из фрагментов. Кроме того, рассмотренные сокеты обеспечивали взаимодействие по UDP, то есть без установки соединения.

Рассмотрим подробнее API для обмена данными, который мы уже использовали в примерах.

Функции sendto() и recvfrom()Служат для отправки и приема дейтаграмм. Используются при обмене данными без соединения.

Прототипы функций:

#include <sys/socket.h>

ssize_t sendto(int socket, const void *message, size_t length,
    int flags, const struct sockaddr *address,
    socklen_t address_len);

ssize_t recvfrom(int socket, void *restrict message, size_t length,
    int flags, struct sockaddr *restrict address,
    socklen_t *restrict address_len);


Параметры этих функций следующие:

  • socket — дескриптор сокета;
  • message — буфер с отправляемым сообщением либо буфер под данные, которые будут приняты;
  • length — длина буфера;
  • flags — флаги, которые рассмотрены далее;
  • address — адрес назначения либо адрес источника данных;
  • address_len — длина адреса.

Результат –1 для функций может означать, что произошла ошибка. В противном случае результат — число отправленных или принятых байтов.

Дейтаграммные сокеты UNIX- и Internet-доменов также разрешают отправлять и принимать дейтаграммы нулевого размера. Принявшая их функция recvfrom() вернет 0, и это не будет ошибкой или завершением работы с клиентом.

ВНИМАНИЕ!Это число не всегда равно объему реально отправленных или принятых дан- ных. В общем случае дейтаграммные протоколы, такие как UDP, для отправки гарантируют, что было отправлено заданное число байтов, не гарантируя получения данных удаленной стороной, или возвращают ошибку.

Но принимающая данные функция recvfrom() может вернуть 0, что говорит о том, что данные отсутствуют. Или, наоборот, вернуть точный размер переданного буфера, что может означать как то, что принято именно столько данных, так и то, что буфера не хватило, а дейтаграмма записана в буфер частично. И это не является ошибкой. А при использовании флага MSG_WAITALL данные принимаются до заполнения буфера.

Флаги, передаваемые функции, как правило, сильно зависят от платформы и не переносимы. Среди них можно выделить следующие, поддерживаемые большинством ОС:

MSG_DONTROUTE — в sendto() не маршрутизировать пакет. Для отправки пакета не будет использован шлюз, то есть данные будут отправлены только на узлы, которые доступны в локальной сети. Используется утилитами диагностики и маршрутизации. Данный флаг определен только для маршрутизируемых семейств протоколов. В Windows некоторые поставщики службы Windows Sockets могут его игнорировать.

MSG_OOB — в sendto() указывает отправить внеполосные данные в сокеты, которые это поддерживают, например типа SOCK_STREAM. Нижележащий протокол также должен поддерживать отправку внеполосных данных. Если опция задана в recvfrom() получающего сокета, внеполосные данные будут приняты.

MSG_PEEK — флаг указывает recvfrom() скопировать в буфер данные без удаления из очереди или внутреннего буфера. Последующий вызов recvfrom() вернет те же самые данные.

MSG_WAITALL — флаг указывает recvfrom() ждать, пока не придет запрошенное количество данных.

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

ПРОЧИЕ ФЛАГИ ДЛЯ ФУНКЦИЙ ОБМЕНА ДАННЫМИ


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

Кроме того, не все флаги работают с функциями sendto()/recvfrom(): некоторые из них устанавливаются только для протоколов, ориентированных на соединение, таких как TCP.

Рассмотрим несколько примеров флагов. Все они отсутствуют в ОС Windows. Флаги, реализованные начиная с версии ядра Linux 2.2:
  • MSG_DONTWAIT — включить неблокирующий режим. Если операция блокируется, возвращается EAGAIN или EWOULDBLOCK. Похож на флаг O_NONBLOCK, но их различие в том, что MSG_DONTWAIT является флагом для отдельного вызова.
  • MSG_EOR — завершить запись, например, для сокетов типа SOCK_SEQPACKET. В доменах SOCK_STREAM и SOCK_DGRAM нет протоколов, его поддерживающих, но они есть в доменах SOCK_SEQPACKET, например Bluetooth, IrDA, X.25.
  • MSG_NOSIGNAL — отключить генерацию сигнала SIGPIPE, если удаленный абонент закрыл соединение. Аналогично установке флага через sigaction(), но для отдельного вызова.

Начиная с версии ядра Linux 2.3.15:
  • MSG_CONFIRM — сообщает канальному уровню, что пересылка состоялась, то есть получен успешный ответ от удаленного абонента. Если канальный уровень не получает его, он регулярно перепроверяет сеть, например, посредством однонаправленной передачи ARP.

Начиная с версии Linux 2.4.4:
  • MSG_MORE — у вызывающего абонента есть данные на передачу. Флаг используется с TCP-сокетами в тех же целях, что и параметр сокета TCP_CORK, с той разницей, что этот флаг может быть установлен для отдельного вызова. Начиная с версии ядра 2.6 поддерживается также и для UDP. В этом случае он используется в том числе для упаковки всех данных в одну дейтаграмму, которая будет отправлена первым вы- зовом sendto() без этого флага. Для Unix-сокетов эта опция не работает.

Прочие флаги:
  • MSG_ZEROCOPY — использовать механизм без копирования данных между ядром и пользовательским пространством. Данный механизм необходимо включать через опцию SO_ZEROCOPY, которая описана в главе 8. Видим, что флаги были реализованы еще в достаточно старых версиях Linux. В новых версиях ядра могут добавляться новые флаги.
  • В разных ОС существуют и достаточно экзотические флаги. Например в IBM AIX 7.2 есть флаг MSG_MPEG2, который указывает, что данные представляют собой блок видеопотока MPEG2.

Весь набор методов класса socket.socket в Python, соответствующих sendto()/recvfrom(), выглядит так:

# Тип буфера, в который может быть произведена запись.
# ReadableBuffer имеет схожее представление.
WriteableBuffer = collections.abc.Buffer
def recvfrom(self, bufsize: int, flags: int) -> tuple[bytes, _RetAddress] def recvfrom_into(self, buffer: WriteableBuffer, nbytes: int, flags: int) -> tuple[int, _RetAddress]
@overload
def sendto(self, data: ReadableBuffer, address: _Address) -> int @overload
def sendto(self, data: ReadableBuffer, flags: int, address: _Address) -> int


Видны следующие особенности:

  • Метод sendto() перегружен, чтобы его сигнатура была такой же, как у функ- ции C API. Вторая сигнатура позволяет вызывать его без флагов.
  • Метод recvfrom_into() позволяет записывать принятые данные в предва- рительно созданный буфер, что соответствует функции recvfrom() C API.


Буфером для вызова recvfrom_into() может являться bytearray, memoryview, массив, различные C-типы и прочие реализации буферного интерфейса:

>>> import socket
>>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
>>> s.bind(('', 12345))
>>> b = bytearray(5)
>>> s.recvfrom_into(b) (5, ('127.0.0.1', 53622))
>>> print(b)
bytearray(b'12345')


Было отправлено 7 байт, но выше был выделен bytearray размером 5 байт, которые и были приняты и скопированы в него из дейтаграммы, «1234567» — ввод с клавиатуры:

➭ nc -u localhost 12345 
1234567


Работа по UDP



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

Предположим, что приложение отправляет какие-то данные, например команду, а затем сразу читает ответ. Если удаленное приложение не получит данные из-за проблем в сети, оно останется в состоянии ожидания команды, а приложение, которое отправило данные, будет бесконечно ожидать ответа.

Подобная проблема решается относительно просто: установкой таймера. Предполагается, что если ответ не поступил в течение некоторого времени, следует прервать ожидание получения данных и отправить команду повторно.

Но это сразу влечет другие проблемы: например, что, если таймер истек, но первая команда не потерялась, а лишь ушла по другому маршруту и поступит позже, чем вторая? Тогда будут получены две команды и отправлены два ответа.

Решением служат сложные протоколы, такие как TCP. О разработке своих протоколов мы поговорим в книге 2.

Эхо-сервер поверх UDP



Рассмотрим код сервера, пример работы которого показан на рис. 4.1. Он просто возвращает клиенту полученный запрос.


int main(int argc, char const *argv[])
{
...
    socket_wrapper::SocketWrapper sock_wrap;
    // Создать новый сокет.
    socket_wrapper::Socket sock = { AF_INET, SOCK_DGRAM, IPPROTO_UDP };
    if (!sock)
   {

     std::cerr << "Socket creation error!" << std::endl; return EXIT_FAILURE;
    }

     auto addrs = socket_wrapper::get_serv_info(argv[1], SOCK_DGRAM);
     // Привязать к сокету адрес.
     if (bind(sock, addrs->ai_addr, addrs->ai_addrlen) != 0)
     {

     std::cerr << "Bind error!" << std::endl; return EXIT_FAILURE;
      }
     std::cout << "Starting echo server on the port " << argv[1] << "...\n";

Между приемом и отправкой может выполняться какая-то работа. По сути, это цикл запроса, обработки и ответа.

Сначала подготовим буферы:

std::array<char, 256> buffer;
// Сюда будет сохранен адрес клиента.
sockaddr_in client_address = {0};
socklen_t client_address_len = sizeof(sockaddr_in); std::array<char, INET_ADDRSTRLEN> client_address_buf;
std::cout << "Running echo server...\n" << std::endl;


В цикле выполним прием и отправку данных:


while (true)
{
        // Принять то, что отправил клиент, в buffer.
        // Адрес клиента будет записан в client_address.
        const ssize_t recv_len = recvfrom(sock, buffer.data(),
                 buffer.size() – 1, 0,
                 reinterpret_cast<sockaddr *>(&client_address), 
                 &client_address_len);
        if (recv_len > 0)
       {

                 buffer[recv_len] = '\0'; 
                 std::cout
                      << "Client with address "
                      << inet_ntop(AF_INET, &client_address.sin_addr,

                            client_address_buf.data(), client_address_buf.size())
                       << ":" << client_address.sin_port
                       << " sent datagram [length = " << recv_len << "]:\n'''\n"
                       << buffer << "\n'''"
                       << std::endl;

               // Тут можно реализовать обработку команд.

              // Отправить принятые ранее данные в ответ клиенту.

             sendto(sock, buffer.data(), recv_len, 0,
             reinterpret_cast<const sockaddr *>(&client_address), client_address_len);
        }
        else if (recv_len < 0) perror("recvfrom");

        std::cout << std::endl;
   }
   return EXIT_SUCCESS;
}

Запустим сервер, а затем, используя Netcat, подключимся и отправим сообщение:

➭
Tes
ncat -u4 localhost 12345 t?
Test?
Test?

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

➭
Sta
build/bin/b01-ch04-udp-server 12345 rting echo server on the port 12345...
Running echo server...
Client with address 127.0.0.1:48185 sent datagram [length = 6]: '''
Test?
'''


Если требуется обрабатывать команды, из строки, принимаемой сервером, необходимо удалить символы перевода строки и возможные концевые пробелы. В Perl для этого использовалась небезызвестная функция chomp(), в Python есть метод строки rstrip(), а в C++ эту функцию приходится реализовывать самостоятельно:


// Убрать «непечатные» символы в конце строки (in place).
static inline void rtrim(std::string &s)
{ 
       s.erase(std::find_if(s.rbegin(), s.rend(), 
    std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

Она нужна из-за того, что netcat передает завершающий перевод строки, а он,
как правило, не требуется.
На Python код будет выглядеть немного лаконичнее:


import socket


# Необходимо задать достаточный размер буфера, хотя он выделяется автоматически. 
# Эта нехарактерная для Python особенность пришла из C.
buffer_size = 255

try:

      s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
     # В Python порт будет автоматически преобразован к сетевому 
      # порядку байтов.
      s.bind(('0.0.0.0', 8080))
      while True:
            # Прием данных и получение адреса.
            data, client_address = s.recvfrom(buffer_size)
            if data:
                # Отправка принятых данных.
                s.sendto(data, client_address) 
except Exception as e:
       print(f'Socket error: {e}')

В результате получаем очень простой эхо-сервер, работающий поверх UDP. Эхо-сервер можно создать и без написания кода, используя стандартную утилиту Netcat:

➭ 
ncat -4 --exec /bin/cat --listen --udp 1230

По этой команде Netcat запустит /bin/cat, начнет прослушивать UDP-порт 1230 и ждать прихода данных. Порядок дальнейших действий следующий:
  • По прибытии данные попадут на стандартный ввод утилиты cat.
  • Она перенаправит их на стандартный вывод.
  • Стандартный вывод будет отправлен в сеть как ответ.

После того как клиент подключится к UDP-порту 1230 и отправит данные, Netcat выведет их на экран:

➭
tes
ncat -4 --udp localhost 1230 t echo server
test echo server


Первое сообщение было введено с консоли. Второе сообщение, test echo server,
пришло как ответ сервера и было напечатано Netcat.


Посмотрите книгу «Сетевое программирование. От основ до приложений» на нашем сайте.

» Оглавление
» Отрывок

По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Для Хаброжителей скидка 25 % по купону — Программирование
Теги:
Хабы:
+15
Комментарии8

Публикации

Информация

Сайт
piter.com
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия