Можно ли посмотреть на пакет дейтаграммы на языках более высокого уровня? - PullRequest
0 голосов
/ 28 октября 2011

В настоящее время я пытаюсь перенести программу 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?

Ответы [ 3 ]

1 голос
/ 28 октября 2011

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

Не уверен, что вы подразумеваете под этим.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;

И когда вы печатаете данные, m->text вычисляет адрес первого символа, поэтому вы можете использовать

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);

Удачи вам с вашимпроект - сетевое программирование - это всегда опыт обучения!

0 голосов
/ 28 октября 2011

UDP-датаграммы ограничены 64 КБ, тогда Ethernet-фреймы составляют 1500 байт (если ваша сеть не использует джамбо-фреймов , которые могут быть до 9000 байт ). Разработчики протоколов обычно стараются избегать фрагментации IP , поэтому, скорее всего, ваши входящие пакеты имеют небольшой размер, то есть меньше 1500 байт.

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

0 голосов
/ 28 октября 2011

Почему бы не сделать это?

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;
...