Есть Сервер и Клиент на неблокирующих сокетах
Клиент:
...
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;
Вобщем, чтобы пакеты не объединялись.
>Есть Сервер и Клиент на неблокирующих сокетах
>Клиент:
>...
>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, он датаграмм-ориентированный. Или расширяй протокол передачи, чтобы каждому блоку данных соответствовал заголовок, в котором указана длина блока.
>Есть Сервер и Клиент на неблокирующих сокетах
>Клиент:
>...
>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 по маршруту пакета.
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.
да, сорри. смотрел на один кусок исходника, а скопировал другой. )
но ведь нам ;-) нужно ВКЛЮЧИТЬ режим TCP_NDELAY, для этого флаг нужно выставить в значение, отличное от 0.
разве не так?
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.
Согласен. На промежуточных роутерах пакеты могут только измельчаться из-за нестыковок MTU, а склеиваться могут только в ядре принимающей стороны. И, думаю, только для stream- протоколов, таких как TCP.
По этому - повторюсь. В любом случае, нельзя рассчитывать на размер отправляемого блока данных.
Очевидный совет автору топика.
Принимайте данные как можно бОльшими кусками, в разумных пределах, конечно. )
Это сократит количество syscalls и снизит нагрузку на ядро.
А получив данные, уже можно разобраться всё получено или нет.
Протокол, в простейшем случае, может выглядеть как, например, четыре байта с размером блока + сам блок данных.
Абсолютно согласен с serge. Флаг TCP_NDELAY лишь указывает отправлять немедленно и не использовать алгоритм объединения в оптимальные для пересылки куски, но совсем не управляет тем, как данные поступят.
Чем больше за раз из сокета сможешь прочитать тем лучше. А парсингом приходящих данных должна заниматься сама программа.
Промежуточное сетевое оборудование (гы кто сказал что только роутеры?) может делать с пакетами все что угодно ;-)) Сам видел.. Знаю, знаю стандарты и все такое.. однако может быть все что угодно. Вот из этого и надо исходить.1. Поэтому надо отказаться от неблокируемых сокетов - плохая идея.
2. Использовать select/poll
3. Написать функцию оболочку ReceivePack для функции recv()
Пусть получает на вход еще и таймаут.
int ReceivePack(int fd, void *buf, int len, int timeout)
{
// bla bla blar_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; // прочитан весь блок
}
Промежуточное сетевое оборудование (кто сказал что будут только роутеры?) может делать с пакетами все что угодно ;-)) Сам видел.
Знаю, знаю стандарты и все такое.. однако может быть все что угодно. Еще и Оська чудить может (гы, будет). Вот из этого и надо исходить.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() обязательно. Были случаи на производстве....
Стивенс. "Сетевое программирование". Узнаете столько нюансов ;-))