UDP-передача в библиотеке lwIP для STM32

UDP-протокол — самый простой протокол для передачи данных, благодаря которому устройства могут обмениваться информацией, не создавая отдельного соединения. Да, это не гарантирует надёжности доставки, но зато вам не требуется инициализация подключения (как в TCP) и хранения данных о нём в дескрипторе соединения. Таким образом экономится как flash, так и оперативная память, и более того передача становится немного быстрее (потому что мы пропускаем этап рукопожатия TCP).

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

Но тогда для чего может быть полезен UDP? Во-первых, это многочисленные протоколы для передачи некритичных данных, таких как Telnet или Syslog. Даже если вы потеряете одну строчку лога какого-то события — зачастую это не стоит того, чтобы запрашивать её вновь (как в TCP).

Ещё этот протокол используется для передачи реалтайм-данных. Если потерялся один из фреймов звукового потока — какой прок с того, что он придёт спустя 100 миллисекунд? В таких случаях проще отбрасывать потерянные пакеты, или попытаться восстановить информацию. Конечно, вы возразите что можно просто сделать буфер на 300-500 миллисекунд звучания, и надеяться что к моменту воспроизведения все пакеты успеют прийти — но это вызовет повышенный расход памяти (с которой в эмбеде всегда сложно), и потребует дополнительной логики обработки. Применимо это не всегда.

Ну и ещё одна область применения UDP — приём большого количества маленьких пакетов данных от массы клиентских устройств. Если в одном пакете передаётся всего несколько десятков-сотен байт, но таких пакетов много — TCP вызовет двойной, а то и тройной перерасход трафика, сводя пропускную способность сети к мизерным величинам. Эти сто байт полезной информации, будучи заключёнными в TCP-IP-Ethernet пакет, ещё и вместе с подтверждающими и перепосланными пакетами, раздуются в итоге до нескольких килобайт, в десятки раз. Конечно, в TCP есть техники для борьбы с перерасходом трафика, но ещё проще посылать всё по UDP.

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

Реализация протокола UDP в lwIP

Первым делом нужно создать дескриптор подключения (да, он всё-таки есть, но очень маленький), и привязать к нему IP и порт.

void UDPTransportInit()
{
UDPSock = udp_new();
udp_recv(UDPSock, NULL, NULL);
udp_bind(UDPSock, IP_ADDR_ANY, 123);
udp_connect(UDPSock, IP_ADDR_ANY, 123);
}

Для отправки данных — просто вызовите функцию udp_sendto. Она принимает дескриптор соединения, указатель на буфер с данными, адрес сервера и номер порта. Я подготовил простую функцию для отправки текстовой строки:

void UDPSend(char* buf, int buf_len)
{
struct pbuf *p;
p = pbuf_alloc(PBUF_TRANSPORT, buf_len, PBUF_RAM);
if (p == NULL) return;
memcpy(p->payload, buf, buf_len);

struct ip_addr server;
IP4_ADDR(&server, 192, 168, 1, 123);

udp_sendto(UDPSyslogSock, p, &server, 123);
pbuf_free(p);
}

Я использую UDP в модуле Syslog, и он очень удобен и занимает мало места в итоговой прошивке.

Ссылка на основную публикацию