Russian Qt Forum
Май 05, 2024, 21:03 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Чтение с сокета без QDataStream  (Прочитано 11831 раз)
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« : Апрель 28, 2011, 14:48 »

Как читать с сокета данные без QDataStream (который используется во всех примерах).
Есть сервер (написан на нативных сокетах под QNX), пишу клиент под Win/Lin под Qt.
Нужно получать с сервера пакеты (пакеты могут иметь разную длину).
Пакет представляет из себя несколько массивов собранных в кучу друг за другом (типы quin32, структуры и т.п.).
при сигнале readyRead() получаю только часть данных (меньше чем нужно в bytesAvailable()).
при socket->read() я так понимаю внутренний буфер очищается на колчиество считанных байт.
Как грамотно реализовать чтение?

я пока делаю так:
Код:
void     QClient::readyRead()
{
    int readBytes = fsocket->bytesAvailable();
    if(readBytes<sizeof(scb_msg_t)) return; //если меньше данных чем заголовок игнорируем

    fsocket->read((char*)frawBuffer,sizeof(scb_msg_t));
    if(fframeBuffer.msg_head->size)
    {
        freadBytes = 0;
        while((readBytes = fsocket->bytesAvailable())!=0)
        {
            fsocket->read((char*)frawBuffer[freadBytes],readBytes);
            freadBytes+=readBytes;
        }
    }
...
}
Записан
RedDog
Гость
« Ответ #1 : Апрель 28, 2011, 15:19 »


Код:
QByteArray arr; // где нидь глобально (всмысле как закрытый член класса)
на readyDead:

Код:
arr.append(socket->readAll());
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #2 : Апрель 28, 2011, 15:22 »

а без QByteArray?

вот текущий пример моей реализации
Код:
if(fblockSize==0)
    {
        if(fsocket->bytesAvailable()<sizeof(scb_msg_t)) return;
        fblockSize = fsocket->read(frawBuffer,sizeof(scb_msg_t));
        if(fframeHeader.size==0)
        {
            fblockSize = 0;
            return;
        }
//        qDebug("block %d\n, header.size %d",fblockSize,fframeBuffer.msg_head->size);
    }

    fblockSize=fsocket->bytesAvailable();
    fleftBytes = fframeBuffer.msg_head->size-sizeof(scb_msg_t);

    if(fblockSize>=(fleftBytes))
    {
        fblockSize = fsocket->read(&frawBuffer[sizeof(scb_msg_t)],fleftBytes);
//        qDebug("fblockSize %d\n",fblockSize);
        fblockSize = 0;
    }
« Последнее редактирование: Апрель 28, 2011, 16:18 от Fregloin » Записан
ilyagoo
Гость
« Ответ #3 : Май 01, 2011, 19:30 »

ну да, qint64 QIODevice::read ( char * data, qint64 maxSize )
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #4 : Июнь 29, 2011, 11:56 »

Вот что у меня получилось.
Код:
void     QGenericArmClient::readyRead()
{
    if(fblockSize==0) //читаем заголовок пакета (остальное остается в буфере)
    {
        if(fsocket->bytesAvailable()<sizeof(scb_msg_t)) return; //если данных меньше чем заголовок - игнорируем
        freceived = fsocket->read(frawBuffer,sizeof(scb_msg_t)); //получаем заголовок
        if(fframeHeader->size==0) //если в заголовке стоит длина 0, игнорируем этот пакет
        {
            freceived = 0;
            return;
        }
    }

    int bytesAvail = fsocket->bytesAvailable(); //сколько еще в буфере данных?
    if(bytesAvail) //если что то есть
    {
        fblockSize = fsocket->read(&frawBuffer[freceived],bytesAvail); //читаем очередной блок данных со смещением на уже принятые
        freceived+=fblockSize; //обновляем колво принятых данных
    }

    if(freceived>=fframeHeader->size) //если данных достаточно
    {
        fmutex->lock();
        qMemCopy(fdestBuffer,frawBuffer,freceived); //копируем в результирующий буфер
        fmutex->unlock();
        emit    frameReceived(freceived); //даем сигнал о том что данные приняты
        freceived = 0; //обнуляем счетчик принятых данных
    }
}
Все работает, но такой вопрос, может ли быть такая ситуация, когда в следующем
Код:
 int bytesAvail = fsocket->bytesAvailable(); //сколько еще в буфере данных?
    if(bytesAvail) //если что то есть
    {
        fblockSize = fsocket->read(&frawBuffer[freceived],bytesAvail); //читаем очередной блок данных со смещением на уже принятые
        freceived+=fblockSize; //обновляем колво принятых данных
    }
может прийти часть следующего пакета?
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #5 : Июнь 29, 2011, 12:25 »

интересно, что некоторые пакеты не приходят, по карайней мере я не получаю сигнал readyRead, когда например в другой программе(которая работает по этому же протоколу но написанной под QNX на socket API) все приходит. В чем может быть проблема?
В данном случае, я ожидаю пакет длиной 48 байт. Ощущение ,что пакеты меньше или равные 48 байтам мне не доходят.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #6 : Июнь 29, 2011, 12:38 »

Цитировать
может прийти часть следующего пакета?
Да, может.
Анализируй заголовок и т.п. через метод peek() к примеру, а потом,
если все проверки прошли, то читай через read().

И вообще, неизвестен твой протокол обмена. Что он из себя представляет?
т.к. алгоритм чтения/парсинга входящего пакета напрямую зависит от протокола.

Раз у тебя в пакете есть заголовок, то ИМХО, разбей архитектуру своего приложения по модели ISO/OSI например на два уровня:
1. Канальный
2. Уровень приложений.

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

Цитировать
В данном случае, я ожидаю пакет длиной 48 байт. Ощущение ,что пакеты меньше или равные 48 байтам мне не доходят.
Ну и что что 48, может придти в один раз 15 байт, а в другой - 80 байт.
Поэтому, нельзя, читая первые байты пакета, думать, что эти данные являются заголовком.. А может заголовок - где-то в середине пакета, а первые байты - это мусор, оставшийся от предыдущего пакета и т.п.

Вот тебе, для ознакомления ссылочка на паттерн при разработки сетевых (да и нетолько) приложений (как один из многих вариантов паттернов):
http://www.eventhelix.com/realtimemantra/patterncatalog/protocol_layer.htm
« Последнее редактирование: Июнь 29, 2011, 12:41 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #7 : Июнь 29, 2011, 13:59 »

Я так и не понял к чему то что по ссылке?
А наш протокол довольно протостой.
Есть заголовок в виде структуры, у которой несколько полей.
Код:
typedef struct
{
        uint32_t family; //Семейство
        uint32_t action; //Действие
        uint32_t size; //размер пакета
        time_t time;
        int32_t p1;
        int32_t p2;
        int32_t p3;
        int32_t p4;
        int32_t p5;
        int32_t p6;
        int32_t p7;
        int32_t p8;
}   scb_msg_t;// __attribute__ ((packed));
Как видно, в size заклдаывается длина пакета (заголовок + данные если есть за ним).
На клиенте мне нужно получить даголовок и данные если есть за ним.
Записан
RedDog
Гость
« Ответ #8 : Июнь 29, 2011, 14:12 »

Прям так структурой и передается?
А если разное выравнивание будет на сервер и клиенте?
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #9 : Июнь 29, 2011, 14:13 »

выравнивание мы привели к одинаковому значению.
например проблем на qnx клиенте не возникает, и все данные приходят как надо. но механизм приема там немного другой в силу специфики ОС.
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #10 : Июнь 29, 2011, 14:19 »

Цитировать
Я так и не понял к чему то что по ссылке?
Очень плохо.

Цитировать
Есть заголовок в виде структуры, у которой несколько полей.
...
Опять 25.
Ты сначала должен определить где же всё-таки находится заголовок в массиве тех байт, которые принял сокет.
Не факт, что первые sizeof(scb_msg_t) байт в буфере будут заголовком.
Иначе можешь оч сильно нарваться на неприятности...

Цитировать
На клиенте мне нужно получить даголовок и данные если есть за ним.
Получай.

Цитировать
выравнивание мы привели к одинаковому значению.
Выравнивание тут ни при чем.
 
Записан

ArchLinux x86_64 / Win10 64 bit
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #11 : Июнь 29, 2011, 14:23 »

предложите ваш вариант?
сервер уже написан (писал не я) и работает.
т.е. в начало и конец заголовка положить сигнатуру?
потом в очередном peek её искать? если нашёл, тогда уже читать как выше изложено?
Записан
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #12 : Июнь 29, 2011, 14:27 »

дело в том, что многие данные из p1-p8 используются, поэтому вырезать заголовок не имеет смысла
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #13 : Июнь 29, 2011, 14:27 »

Цитировать
т.е. в начало и конец заголовка положить сигнатуру?
потом в очередном peek её искать? если нашёл, тогда уже читать как выше изложено?
аха.
ищем заголовок, смотрим сколько там указано байт данных после него, прыгаем туда, и ищем после данных конечную сигнатуру.
и если всё ок, то пакет вынимаем.

если же пакет не полный пришел, то ждем пока он наполнится, а потом уже анализируем

Цитировать
дело в том, что многие данные из p1-p8 используются, поэтому вырезать заголовок не имеет смысла
значит не вырезай

В общем, думай сам. Тебе виднее как у вас там что сделано.
« Последнее редактирование: Июнь 29, 2011, 14:31 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #14 : Июнь 29, 2011, 14:35 »

Цитировать
т.е. в начало и конец заголовка положить сигнатуру?
бррр...
и в заголовок сигнатуру и в конец пакета.

например: <0x68><len><len><0x68><data><0x16>

В общем, что-то типа кадров формата FT xxx сделай.
ИМХО, так будет надежнее чем то что есть сейчас.

Нечего придумывать новое, если все уже придумано за вас.

PS: можно еще и CRC впихнуть
« Последнее редактирование: Июнь 29, 2011, 14:39 от kuzulis » Записан

ArchLinux x86_64 / Win10 64 bit
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.047 секунд. Запросов: 23.