URL: https://www.opennet.me/cgi-bin/openforum/vsluhboard.cgi
Форум: vsluhforumID9
Нить номер: 4652
[ Назад ]

Исходное сообщение
" объединение отсылаемых пакетов"

Отправлено kowak , 15-Сен-05 17:39 
Есть Сервер и Клиент на неблокирующих сокетах
Клиент:
...
send(sock, str1, len1, 0);
send(sock, str2, len2, 0);

Сервер:
...
Sleep(5000);
read_data = recv(sock, str, BUF_SIZE, 0);

При одновременном запуске, Сервер получит read_data==len1+len2 (если len1+len2<=FUB_SIZE),
а нужно чтобы read_data==len1;
Вобщем, чтобы пакеты не объединялись.


Содержание

Сообщения в этом обсуждении
" объединение отсылаемых пакетов"
Отправлено Alexander S. Salieff , 15-Сен-05 20:48 
>Есть Сервер и Клиент на неблокирующих сокетах
>Клиент:
>...
>send(sock, str1, len1, 0);
>send(sock, str2, len2, 0);
>
>Сервер:
>...
>Sleep(5000);
>read_data = recv(sock, str, BUF_SIZE, 0);
>
>При одновременном запуске, Сервер получит read_data==len1+len2 (если len1+len2<=FUB_SIZE),
>а нужно чтобы read_data==len1;
>Вобщем, чтобы пакеты не объединялись.

Если это TCP то они могут не только объединяться, но и разъединяться на сегменты любой длины. Любой сильно интеллектуальный роутер (или стек ОСи) может склеить несколько пакетов  в кучку или разбить один пакет на части. Это же поточный протокол, он не обязан согранять границы дейтаграмм. Если тебе не важна гарантия доставки, юзай UDP, он датаграмм-ориентированный. Или расширяй протокол передачи, чтобы каждому блоку данных соответствовал заголовок, в котором указана длина блока.


" объединение отсылаемых пакетов"
Отправлено serge , 16-Сен-05 17:51 
>Есть Сервер и Клиент на неблокирующих сокетах
>Клиент:
>...
>send(sock, str1, len1, 0);
>send(sock, str2, len2, 0);
>
>Сервер:
>...
>Sleep(5000);
>read_data = recv(sock, str, BUF_SIZE, 0);
>
>При одновременном запуске, Сервер получит read_data==len1+len2 (если len1+len2<=FUB_SIZE),
>а нужно чтобы read_data==len1;
>Вобщем, чтобы пакеты не объединялись.

отключите буферизацию на клиенте.
flags = fcntl(fd, F_GETFL, 0);
rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
но делать так - плохо. )
протокол уровня прилижения работающий поверх TCP не должен быть завязан на размер блока, т.к. TCP - поточный протокол, а не пакетный.
Имейте ввиду, может получиться так, что один send/write() на отправляющей стороне может привратится в два recv/read меньшего размера на принимающей. Это зависит от нагрузки на ядро в этот момент и размера MTU по маршруту пакета.


" объединение отсылаемых пакетов"
Отправлено genie , 20-Сен-05 04:29 
Ne sovsem tak - nado otkluchat' Nagle:

int zero=0;
setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(unsigned char *)&zero,sizeof(int));

Hotya eto tol'ko lokal'nyi kontrol' - kak pravil'no zametili, routers mogut eti pakety kombinirovat' po svoemu usmotreniu.


" объединение отсылаемых пакетов"
Отправлено serge , 20-Сен-05 04:57 
да, сорри. смотрел на один кусок исходника, а скопировал другой.  )
но ведь нам ;-) нужно ВКЛЮЧИТЬ режим TCP_NDELAY, для этого флаг нужно выставить в значение, отличное от 0.
разве не так?

" объединение отсылаемых пакетов"
Отправлено genie , 20-Сен-05 05:02 
Ugu, pral'na...

A esche ya glupost' napisal - ne mogut routers pakety skleivat', poetomu vse eto dobro budet idti, kak ono est'. Bol'shinstvo TCP stacks dlya ertogo PUSH flag ispol'zuut.


" объединение отсылаемых пакетов"
Отправлено serge , 20-Сен-05 05:25 
Согласен. На промежуточных роутерах пакеты могут только измельчаться из-за нестыковок MTU, а склеиваться могут только в ядре принимающей стороны. И, думаю, только для stream- протоколов, таких как TCP.
По этому - повторюсь. В любом случае, нельзя рассчитывать на размер отправляемого блока данных.
Очевидный совет автору топика.
Принимайте данные как можно бОльшими кусками, в разумных пределах, конечно. )
Это сократит количество syscalls и снизит нагрузку на ядро.
А получив данные, уже можно разобраться всё получено или нет.
Протокол, в простейшем случае, может выглядеть как, например, четыре байта с размером блока + сам блок данных.


" объединение отсылаемых пакетов"
Отправлено Hordi , 20-Сен-05 13:16 
Абсолютно согласен с serge. Флаг TCP_NDELAY лишь указывает отправлять немедленно и не использовать алгоритм объединения в оптимальные для пересылки куски, но совсем не управляет тем, как данные поступят.
Чем больше за раз из сокета сможешь прочитать тем лучше. А парсингом приходящих данных должна заниматься сама программа.

" объединение отсылаемых пакетов"
Отправлено enot , 21-Сен-05 14:20 
Промежуточное сетевое оборудование (гы кто сказал что только роутеры?) может делать с пакетами все что угодно ;-)) Сам видел.. Знаю, знаю стандарты и все такое.. однако может быть все что угодно. Вот из этого и надо исходить.

1. Поэтому надо отказаться от неблокируемых сокетов - плохая идея.
2. Использовать select/poll
3. Написать функцию оболочку ReceivePack для функции recv()
Пусть получает на вход еще и таймаут.
int ReceivePack(int fd, void *buf, int len, int timeout)
{
  // bla bla bla

  r_len = 0;
  for (int size = 0 ; size < len; size += r_len) {

    select(...) /// timeout

    if (данные мона читать?) {
// ждем блок, если меньше повторим
      r_len = recv(fd, buf, len-size,.....); // ждем блок, если меньше повторим
      if (r_len <0) { /// errors ; return -1; }
      size+=r_len;
    }
  }
  return 0; // прочитан весь блок
}


" объединение отсылаемых пакетов"
Отправлено enot , 21-Сен-05 14:42 
Промежуточное сетевое оборудование (кто сказал что будут только роутеры?) может делать с пакетами все что угодно ;-)) Сам видел.
Знаю, знаю стандарты и все такое.. однако может быть все что угодно. Еще и Оська чудить может (гы, будет). Вот из этого и надо исходить.

1. Поэтому надо отказаться от неблокируемых сокетов - плохая идея.
Это ж непрерывное переключение контекста...
2. Использовать select/poll . Чтоб не висеть если ничего не придет.
3. Написать функцию оболочку ReceivePack для функции recv()
Пусть получает на вход еще и таймаут. И ее использовать для приема пакетов. Сильно схематично:

// Получает полностью блок данных заданного размера len,
// либо ошибку, либо таймаут.
int ReceivePack(int fd, void *buf, int len, int timeout)
{
  // bla bla bla

  // читаем пока не вычитаем весь блок заданной длины len,
  // иначе вылетаем по тайму - соединение в ауте
  for (int size = 0, r_len = 0; size < len; size += r_len) {
    r_len = 0; // гы

    select(...); // Ждем появления данных, иначе
    // селект вернет либо ошибку, либо тайм
    // тайм - return 1;
    // ошибка - return -1;

    if (данные мона читать?) { // FD_ISSET(...) == ...
      // читаем блок
      r_len = recv(fd, buf, len - size,...);
      if (r_len < 0) {
        // ошибка либо прерывание сигналом, либо что-то тяжелое :-((
        //
        return -1;
      }
    }
  }
  return 0; // прочитан весь блок. Зачет
}

4. Сделать то же самое для send() обязательно. Были случаи на производстве....

Стивенс. "Сетевое программирование". Узнаете столько нюансов ;-))