crossplatform.ru

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


  Ответ в "Склеивание" сообщений ТСР
Введите ваше имя
Подтвердите код

Введите в поле код из 6 символов, отображенных в виде изображения. Если вы не можете прочитать код с изображения, нажмите на изображение для генерации нового кода.
 

Опции сообщения
 Включить смайлы?
Иконки сообщения
(Опционально)
                                
                                
  [ Без иконки ]
 


Последние 10 сообщений [ в обратном порядке ]
pwp2008 Дата 29.10.2015, 19:14
 
Цитата(ViGOur @ 28.10.2015, 9:41) *
1. Почему нет? Ты можешь считать за один раз сколько тебе нужно, а там уже разобрать эти данные на 1 байт + данные + склеенное сообщение (если будет и отправить склеенной для следующего разбора)

----------------------------------------------------------------------------------------
По обработке склеенных\разделенных блоков ТСР в Qt 4.5. Привожу лишь существенный код.
Файл - mainwin_02.h
class MainWin_02 : public QMainWindow
{
    Q_OBJECT
public:
    QByteArray srv_R1;   // для ответа сервера
    QQueue <QByteArray> queueMsg;
    void handleQueue();
    bool f_queue;
..........................
public slots:
    void slotGetResponse();
};

Файл - mainwin_02.cpp
MainWin_02::MainWin_02(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWin_02)
{
................................................
//------------------------------------
    // Описание сокета.
    m_pTcpSocket = new QTcpSocket(this);
    connect(m_pTcpSocket,SIGNAL(connected()),SLOT(slotConnected()));
    connect(m_pTcpSocket,SIGNAL(readyRead()),SLOT(slotGetResponse()));
//------------------------------------
void MainWin_02::slotGetResponse()
{
    // читаем склееные и неполные сообщения по сигналу SIGNAL(readyRead()
    int k1,tLen;
    int kk = m_pTcpSocket->bytesAvailable();
    tLen = kk;
    bool f_queue = false;    // признак постановки в очередь
    while (tLen >0) {
        srv_R1.clear();
        srv_R1 = m_pTcpSocket->peek(1);
        k1= srv_R1[0];
        if (tLen >=k1) {
            srv_R1.clear();
            srv_R1 = m_pTcpSocket->read(k1+1);  // читаем вместе с длиной
            tLen = tLen-(k1+1);
            queueMsg.enqueue(srv_R1);               // пишем в очередь
            f_queue = true;
        } else break;    // выйдем, если неполное сообщение и ждем остаток
    }
    if (f_queue) {
        // выполнить обработку очереди
        handleQueue();
    }
}
void MainWin_02::handleQueue()
{
    // обработка очереди сообщений
    while (!queueMsg.isEmpty()) {
        srv_R3.clear();
        srv_R3 = queueMsg.dequeue();
        // далее обработка сообщения ..................................
        qDebug() << "r3="<< srv_R3.toHex() <<srv_R3.data()<< "ostatok="<<queueMsg.count();
    }
}

ViGOur Дата 28.10.2015, 9:41
  1. Почему нет? Ты можешь считать за один раз сколько тебе нужно, а там уже разобрать эти данные на 1 байт + данные + склеенное сообщение (если будет и отправить склеенной для следующего разбора)
2. Синхронизация, используй QMutex, чтобы пока один поток не отработал, другой не вошел для чтения.

Метод recv был для примера, ты можешь так же в место него использовать read... :)
pwp2008 Дата 27.10.2015, 19:49
 
Цитата(ViGOur @ 27.10.2015, 10:30) *
4 байта было сказано для примера, ты сам решай, какой длины будет у тебя поле длина пакета. К тому же жестко указывать размер нельзя, а только sizeof(int), так как под разными системами int разного размера.
............................

Большое спасибо за пример. Еще, если можно, ряд уточнений.
1. Для моей задачи длина сообщений от 14 до 130 байт, поэтому вполне для длины хватит и 1-го байта.
Прилично ли считывать 1 байт? Хотя, судя по тому, что и 4 байта могут идти частями, думаю, что вполне
прилично.
2. Посоветуйте, какая технология может быть, чтобы не было 2-го входа в слот по сигналу ReadyRead ?
Ведь вполне возможно, что пока я буду выделять и возможно обрабатывать "склееные" сообщения и\или
ждать пока соберется все "разрезанное" сообщение, система может мне устроить второй вход в незаконченный
слот. Реентерабельность тут как то не просматривается.
Не снимать же connect по входу в слот обработки и назначать его потом снова перед выходом?
signal(readyRead)\slot -> обработка сообщений ТСР по идеологии Qt.
Я конечно попробую разобраться с вышеприведенным примером, т.к. в QT для сокета нет метода recv.
Влад Дата 27.10.2015, 10:33
 
Цитата(pwp2008 @ 26.10.2015, 19:49) *
Т.е. 4 байта уж точно не разрежутся ? Вообще, можно и 2-мя обойтись, надежней.
Сколько ждать полного буфера, длина, которого была заявлена в заголовке, ведь можем и не дождаться?
Надеюсь, что ТСР не переставит блоки, а будет все-таки давать их в порядке отправления.
Контрольная сумма меня особенно радует.

Разрежутся или нет четыре байта длины - никто тебе не даст никаких гарантий. Да это и не имеет значения. Просто вычитывай из сокета, пока не наберутся в начале посылки эти четыре байта. А потом вычитывай столько байтов, сколько заявлено в заголовке. Либо они дойдут, либо ты получишь ошибку связи и должен на нее как-то отреагировать. Да, TCP не переставит блоки, в этом можно быть уверенным.
ViGOur Дата 27.10.2015, 10:30
  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 пункту
pwp2008 Дата 26.10.2015, 19:49
 
Цитата(ViGOur @ 26.10.2015, 17:17) *
pwp2008, вот ты упрямец! Перечитай заново, что я описал выше. :)

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

Т.е. 4 байта уж точно не разрежутся ? Вообще, можно и 2-мя обойтись, надежней.
Сколько ждать полного буфера, длина, которого была заявлена в заголовке, ведь можем и не дождаться?
Надеюсь, что ТСР не переставит блоки, а будет все-таки давать их в порядке отправления.
Контрольная сумма меня особенно радует.
Что ж, видимо с этим придется жить... Спасибо за помощь.
Есть такой протокол - МЭК 104. Похоже нужно от него брать ряд идей.
ViGOur Дата 26.10.2015, 17:17
  pwp2008, вот ты упрямец! Перечитай заново, что я описал выше. :)

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

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

Правильный подход тебе описал коллега ViGOur.
pwp2008 Дата 25.10.2015, 18:11
 
Цитата(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 по технологии Клиент-Сервер.
ViGOur Дата 20.10.2015, 21:03
  Ты не понял.
TCP стеку все равно, что в него попадает видео, звук или сообщения, он как получил так и отдал, твоя забота уже правильно разобрать переданые тобой данные.
За пример можно взять лог файл, если не сделать правильное форматирование, то логи будут не читабельны, то же самое и здесь.

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

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

Или чтобы не писать свой протокол над TCP, отправлять сообщения одной длины, например 1024 байта...
Просмотр темы полностью (откроется в новом окне)
RSS Текстовая версия Сейчас: 16.11.2019, 4:58