Чи можна переглянути пакунок дейтаграми на мовах вищого рівня?

В даний час я намагаюся приєднати програму c, яка стосується пакета дейтаграми (udp) до однієї мови вищого рівня. Оскільки пакети можуть мати змінну величину, вони починаються з цілого числа із зазначенням їх розміру. У c я закликаю recv з прапорцем MSG_PEEK, щоб спочатку отримати це значення, потім виділити придатний буфер і прочитати решту пакета. Код (спрощений) виглядає так:

// Simplified message format.
struct message {
    int length;
    char[] text;
}
struct message *m = malloc (sizeof(int));

// Read out in just length.
recv (sock, m, sizeof(int), MSG_WAITALL | MSG_PEEK);
int txtlen = ntohl (m->length) * sizeof(char);
int msglen = sizeof(int) + txtlen;

// Read complete packet.
m = realloc (m, msglen);
read (sock, m, msglen);
m->text[txtlen] = '\0';

// Show result.
printf("%s\n", &m->text);

Я хочу уникнути, здається, загальноприйнятої практики, щоб виділити величезний буфер і сподіватися, що не прийдуть більші пакети. Так щось подібне до того, як переглянути датаграму або визначити її повну довжину заздалегідь можливо на мовах вищого рівня, як python або java?

0
Якщо це не вбудована система з дуже лімітованими ресурсами, то, напевно, ні.
додано Автор Karoly Horvath, джерело
Пакети UDP не можуть бути "величезними" (див. Наприклад, stackoverflow.com/questions/1098897/ … ). Ви впевнені, що ви повинні турбуватися про це?
додано Автор Mat, джерело

3 Відповіді

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

Не впевнений, що ви маєте на увазі під цим. Пакет UDP надходить одразу, тому початкове ціле число говорить точно, наскільки великий ваш буфер; він не "росте" після прибуття.

Оскільки ви додаєте нульовий символ, вам слід врахувати це у розрахунку довжини:

int msglen = sizeof(int) + txtlen + 1;

Будьте обережні, коли ви використовуєте realloc() :

m = realloc (m, msglen);

Якщо realloc не вдається, він встановить m на нуль. Це означає, що ви втратите свою єдину посилання на пам'ять, яка була спочатку призначена для цього, тому ви ніколи не зможете його free() . Спробуйте щось на зразок цього:

void *tmp = realloc(m, msglen)
if (tmp == null) {
 //handle the error
}
m = tmp;

And when you print the data, m->text evaluates to the address of the first character, so you can use

printf("%s\n", m->text);

Крім того, ви можете визначити вашу структуру з фіксованим розміром, як

struct message {
  int length;
  char *text;
}

Потім ви можете використовувати malloc() , щоб виділити (лише) ваш текстовий буфер:

struct message m;
recv(sock, &m.length, sizeof(int), MSG_WAITALL | MSG_PEEK);
m.text = malloc(m.length + 1);//+1 for the null that you'll append
read(sock, m.text, m.length);
m.text(m.length) = '\0';

printf("%s\n", m.text);
free(m.text);

Успішність з вашим проектом - мережеве програмування - це завжди навчальний досвід!

1
додано
Дякую за ловлю! Я зробив виправлення в моїй відповіді.
додано Автор Adam Liss, джерело
Я утримався від перевірки realloc в прикладі коду, щоб тримати його простим. Як зазначено вище, дві читальні дзвінки будуть читати два пакети, і тому я віддаю перевагу структурі.
додано Автор XZS, джерело

UDP datagrams are limited to 64K, then ethernet frames are 1500 bytes (unless your network is using jumbo frames, which could be up to 9000 bytes). Protocol designers usually try to avoid IP fragmentation, so most likely your incoming packets are small, i.e. less then 1500 bytes.

Я б просто почав зі статичного буфера з 1472 (1500 Ethernet-кадрів довжиною - 20 байт IP-заголовка - 8 байтів заголовка UDP). Якщо вам доведеться мати справу з деякими довільними протоколами - підніміть їх до 64K. Якщо ви цього не можете собі дозволити - зібрати фактичні розміри за допомогою MSG_PEEK , знайдіть зручний середній показник і встановіть програму скидання за допомогою malloc (3) .

0
додано
Так, 64K, безумовно, марнотратна, але в цей день і вік це дійсно не є проблемою, якщо ви не перебуваєте в деяких вбудованих/ресурсозв'язаних середовищах.
додано Автор Nikolai Fetissov, джерело
Я очікую, що ліміт 64K випливає з 16-бітового поля довжини UDP, який визначає довжину пакету. Тому це має справді дбати про буфер збереження. Але навіть 64K фактично є відмиранням, враховуючи, що потрібна пам'ять може бути раніше відома, прочитавши лише той інтерфейс.
додано Автор XZS, джерело

Чому б це не зробити?

message = (struct message *)malloc(sizeof(struct message));
read(sock, &message->length, sizeof(int);
message->length = ntohl(message->length);
message->text = (char *)malloc(message->length + 1);
read(sock, message->text, message->length);
message->text[message->length] = 0;
0
додано
На жаль, це не спрацює, оскільки він отримує два дейтаграми. При першому читанні вся дейтаграма відхиляється, навіть якщо вона містить більше, ніж просто int.
додано Автор XZS, джерело