crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

2 страниц V   1 2 >  
Ответить в данную темуНачать новую тему
> "Склеивание" сообщений ТСР
pwp2008
  опции профиля:
сообщение 19.10.2015, 19:31
Сообщение #1


Студент
*

Группа: Участник
Сообщений: 29
Регистрация: 19.12.2014
Пользователь №: 4299

Спасибо сказали: 0 раз(а)




Репутация:   0  


Подскажите пож-та, встречалось ли Вам отправленные друг за другом сообщения (клиентом) получать в сервере "склееном" виде, как если бы это было одно сообщение. В чем причина такой работы по протоколу TCP\IP ? Разрулить эту проблему удалось несколько искусственным способом, хотя здесь может подойти и передача длины сообщения и/или контрольный суммы. Видимо так склеены могут быть не 2, а даже больше последовательных сообщений.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ViGOur
  опции профиля:
сообщение 20.10.2015, 9:09
Сообщение #2


Мастер
******

Группа: Модератор
Сообщений: 3296
Регистрация: 9.10.2007
Из: Москва
Пользователь №: 4

Спасибо сказали: 231 раз(а)




Репутация:   40  


Это нормально. Пакет может прийти неполный или склеенный с другим, а то и с несколькими.
Для того, чтобы нормально разбирать пакеты обычно для их отправки используют свою структуру данных, что-то вроде:
struct sTcpData{
    int nLen;    // Размер буфера
    char *pBuf;  // Сам буфер
}
По желанию можно добавить CRC

TCP буфер можно расценивать как файл, в который пишутся данные одним потоком, а из другого потока ты эти данные разбираешь.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
pwp2008
  опции профиля:
сообщение 20.10.2015, 19:09
Сообщение #3


Студент
*

Группа: Участник
Сообщений: 29
Регистрация: 19.12.2014
Пользователь №: 4299

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(ViGOur @ 20.10.2015, 9:09) *
Это нормально. Пакет может прийти неполный или склеенный с другим, а то и с несколькими.
.............................................
TCP буфер можно расценивать как файл, в который пишутся данные одним потоком, а из другого потока ты
эти данные разбираешь.

Ну да, из практики вроде оно так и есть. Но ведь все таки по каким то правилам входной поток то прерывается ?
Опять же, там вроде 7 или 8 уровней в протоколе ТСР и над ними опять нужно свои уровни добавлять, тем более с
контрольными суммами ? Как то не ice.....
По моему здесь как то завязана gui-технология обработки, пока приложение не выйдет на ожидание событий-поток
в ТСР не прерывается, стоит только это организовать - сообщения перестают склеиваться. Может вообще вынести
запись в сокет в отдельный thread (надо бы попробовать).
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ViGOur
  опции профиля:
сообщение 20.10.2015, 21:03
Сообщение #4


Мастер
******

Группа: Модератор
Сообщений: 3296
Регистрация: 9.10.2007
Из: Москва
Пользователь №: 4

Спасибо сказали: 231 раз(а)




Репутация:   40  


Ты не понял.
TCP стеку все равно, что в него попадает видео, звук или сообщения, он как получил так и отдал, твоя забота уже правильно разобрать переданые тобой данные.
За пример можно взять лог файл, если не сделать правильное форматирование, то логи будут не читабельны, то же самое и здесь.

Есть 3 варианта того, какими ты можешь получить свои сообщения:
1. Не полное сообщение (в том случае, если ты забираешь данные быстрей, чем они приходит)
2. Полное сообщения (идеальное стечение обстоятельств)
3. Склеенное сообщение (в том случае, если ты забираешь медленней, чем они приходят и склееным может быть как часть следующего пакета, так и несколько пакетов...)

Обычно, чтобы всегда были идеальные условия используют структуру приведенную мной выше, по следующей схеме:
1. При поступленни данных читаем первые 4 байта, и приводим их к int, чтобы получить размер буфера.
2. Читаются столько байт, сколько указанно в первых четырех байтах полученных в 1 пункте.
3. Что-то там делаем с полученными данными.
4. Переходим к 1 пункту.

Или чтобы не писать свой протокол над TCP, отправлять сообщения одной длины, например 1024 байта...
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
pwp2008
  опции профиля:
сообщение 25.10.2015, 18:11
Сообщение #5


Студент
*

Группа: Участник
Сообщений: 29
Регистрация: 19.12.2014
Пользователь №: 4299

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(ViGOur @ 20.10.2015, 21:03) *

Наработка по "склеиванию" сообщений такова :
Имеем 2 простых приложения : 1-Сервер 2-Клиент (хотя кто из них Сервер, кто Клиент
-чисто условное деление). Сервер по кнопке на своем окне высылает клиенту сообщение
по сети ТСР\IP. Соединение между ними было преварительно установлено : сервер
ждал соединения, Клиент - соединился и ждет сообщений от сервера. После получения
его выдает его в свое окно. Так вот, если сообщение сервера оформлено как одно:
string parcel_x = "wwwwwwwwwwwwwwwwwwwwwwwwwwwww";
int numBytes = firstClientSocket->write(parcel_x.data(),parcel_x.length());
то Клиент его получает и показывает. Если же поставить 2 сообщения подряд :

string parcel_x = "wwwwwwwwwwwwwwwwwwwwwwwwwwwww";
int numBytes = firstClientSocket->write(parcel_x.data(),parcel_x.length());
string parcel_z = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
int numBytes = firstClientSocket->write(parcel_z.data(),parcel_z.length());
......................................
то Клиент получает только одно, но "склеенное" из 2-х сообщение. Хотелось бы
понять причины этого и по возможности "разделить" сообщения, чтобы они приходили
на клиент в виде 2 сообщений.
Проверено следующее :
Если второе сообщение выполнить по слоту однократного таймера через ххх мсек,
то тогда склейки на клиенте нет :
string parcel_x = "wwwwwwwwwwwwwwwwwwwwwwwwwwwww";
int numBytes = firstClientSocket->write(parcel_x.data(),parcel_x.length());
int xxx = 50; // интервал для срабатывания
QTimer::singleShot(xxx,this,SLOT(slotMessage_2()));
..............................

void PRG::slotMessage_2()
{
string parcel_z = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
int numBytes = firstClientSocket->write(parcel_z.data(),parcel_z.length());
}
и каждое сообщений приходит отдельно. Причем , уже при xxx > 11 .
Если xxx=1, то сообщения "склеиваются" и в таком исполнении.
Явное указание в сервере типа соединения = Qt::DirectConnection в методе connect :
bool QObject::connect ( const QObject * sender, const char * signal,
const QObject * receiver, const char * method,
Qt::ConnectionType type = Qt::AutoConnection ) [static]

"склеивания" не убирает( похоже может на Клиенте нужно было так же сделать ? на Клиенте я не ставил, упустил)
Тексты приложений не привожу - это просто пример из Qt по технологии Клиент-Сервер.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Влад
  опции профиля:
сообщение 26.10.2015, 14:45
Сообщение #6


Участник
**

Группа: Участник
Сообщений: 146
Регистрация: 20.3.2009
Из: Санкт-Петербург
Пользователь №: 627

Спасибо сказали: 46 раз(а)




Репутация:   8  


Цитата(pwp2008 @ 25.10.2015, 18:11) *
......Клиент получает только одно, но "склеенное" из 2-х сообщение. Хотелось бы
понять причины этого и по возможности "разделить" сообщения, чтобы они приходили на клиент в виде 2 сообщений.
......
и каждое сообщений приходит отдельно. Причем , уже при xxx > 11 .
Если xxx=1, то сообщения "склеиваются" и в таком исполнении.

Еще раз: протокол TCP ничего не "знает" ни о каких сообщениях. TCP просто передает поток байтов от передатчика (передающей стороны) к приемнику. Все.
Как он будет "склеивать" или наоборот "разрезать" твои сообщения - никто не дает никаких гарантий. В зависимости от загрузки сети при одном и том же исходном коде и одних и тех же таймаутах может быть и "склеивание", и "разрезание" сообщений - по внутреннему усмотрению драйвера TCP. Поэтому подбирать какие-либо таймауты, слоты и прочее - бессмысленно.

Правильный подход тебе описал коллега ViGOur.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ViGOur
  опции профиля:
сообщение 26.10.2015, 17:17
Сообщение #7


Мастер
******

Группа: Модератор
Сообщений: 3296
Регистрация: 9.10.2007
Из: Москва
Пользователь №: 4

Спасибо сказали: 231 раз(а)




Репутация:   40  


pwp2008, вот ты упрямец! Перечитай заново, что я описал выше. :)

Таймауты тебе могут помочь в том случае, если сервер или клиент не заняты, но если один из них занят (I\O операции или по процессору) то всеравно будет склейка и тайм ауты не помогут. Это из серии я надеюсь всё будет в порядке, но в программировании это не допустимо, потому нужно создавать условия, чтобы всё было в порядке.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
pwp2008
  опции профиля:
сообщение 26.10.2015, 19:49
Сообщение #8


Студент
*

Группа: Участник
Сообщений: 29
Регистрация: 19.12.2014
Пользователь №: 4299

Спасибо сказали: 0 раз(а)




Репутация:   0  


Цитата(ViGOur @ 26.10.2015, 17:17) *
pwp2008, вот ты упрямец! Перечитай заново, что я описал выше. :)

Не упрямый, а просто - неграмотный. Стаж по диалогу в TCP у меня небольшой, может год- полтора, на 2х или 3х приложениях, просто я ни разу до текущего приложения не попадал с такие ситуации("склеивание\разрезание").
Это, конечно, не довод. Но от всей этой ситуации повеяло безнадежностью :
Цитата(ViGOur)
Обычно, чтобы всегда были идеальные условия используют структуру приведенную мной выше, по следующей схеме:
1. При поступленни данных читаем первые 4 байта, и приводим их к int, чтобы получить размер буфера.
2. Читаются столько байт, сколько указанно в первых четырех байтах полученных в 1 пункте.
3. Что-то там делаем с полученными данными.
4. Переходим к 1 пункту.

Т.е. 4 байта уж точно не разрежутся ? Вообще, можно и 2-мя обойтись, надежней.
Сколько ждать полного буфера, длина, которого была заявлена в заголовке, ведь можем и не дождаться?
Надеюсь, что ТСР не переставит блоки, а будет все-таки давать их в порядке отправления.
Контрольная сумма меня особенно радует.
Что ж, видимо с этим придется жить... Спасибо за помощь.
Есть такой протокол - МЭК 104. Похоже нужно от него брать ряд идей.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ViGOur
  опции профиля:
сообщение 27.10.2015, 10:30
Сообщение #9


Мастер
******

Группа: Модератор
Сообщений: 3296
Регистрация: 9.10.2007
Из: Москва
Пользователь №: 4

Спасибо сказали: 231 раз(а)




Репутация:   40  


4 байта было сказано для примера, ты сам решай, какой длины будет у тебя поле длина пакета. К тому же жестко указывать размер нельзя, а только sizeof(int), так как под разными системами int разного размера.
4 байта могут так же прийти по частям, это маловероятно, но может быть, например при медленном соединении или при крайней загруженности системы или сети.
Блоки не переставляются и контрольные суммы по идее не нужны, так как TCP протокол гарантирует доставку пакета
Набросал на коленке пример реализации:
struct sDataPacket
{
    int nLen;
    char *pData;
};

sDataPacket *getPacket( int socket )
{
    sDataPacket *pPacket = new sDataPacket;
    // Читаем первые 4 байта
    int nLen = sizeof(pPacket->nLen);
    char *pBuff = new char[nLen];
    do
    {
        int nRet = recv( socket, pBuff, nLen, 0 );
        if( nRet == sizeof(pPacket->nLen) )
        {
            // Переводим прочитанные 4 байта в длину
            memcpy((void*)&pPacket->nLen, (void*)pBuff, nLen);
            // Читаем указанное количество байт данных
            if( pPacket->nLen > 0 )
            {
                pPacket->pData = new char[pPacket->nLen];
                nRet = recv( socket, pPacket->pData, pPacket->nLen, 0 );
                if( nRet < pPacket->nLen )
                {
                    // Дочитываем
                }
            }
            break;
        }
        else
        {
            // Дочитываем
        }
    }while(true);
    delete []pBuff;

    return pPacket;
}
Тебе только осталось дописать докачку...

Как вариант, во избежании склеивания, можно сделать клиент серверный диалог с подтверждением, например:
1. Клиент отправляет пакет
2. Сервер получает пакет
3. Сервер отправляет подтверждение о получении пакета
4. Клиент получает подтверждени и идет к 1 пункту
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Влад
  опции профиля:
сообщение 27.10.2015, 10:33
Сообщение #10


Участник
**

Группа: Участник
Сообщений: 146
Регистрация: 20.3.2009
Из: Санкт-Петербург
Пользователь №: 627

Спасибо сказали: 46 раз(а)




Репутация:   8  


Цитата(pwp2008 @ 26.10.2015, 19:49) *
Т.е. 4 байта уж точно не разрежутся ? Вообще, можно и 2-мя обойтись, надежней.
Сколько ждать полного буфера, длина, которого была заявлена в заголовке, ведь можем и не дождаться?
Надеюсь, что ТСР не переставит блоки, а будет все-таки давать их в порядке отправления.
Контрольная сумма меня особенно радует.

Разрежутся или нет четыре байта длины - никто тебе не даст никаких гарантий. Да это и не имеет значения. Просто вычитывай из сокета, пока не наберутся в начале посылки эти четыре байта. А потом вычитывай столько байтов, сколько заявлено в заголовке. Либо они дойдут, либо ты получишь ошибку связи и должен на нее как-то отреагировать. Да, TCP не переставит блоки, в этом можно быть уверенным.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

2 страниц V   1 2 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 19.3.2024, 8:17