Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: [РЕШЕНО] чтение sizeof( int ) в QTcpSocket
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Ввод/Вывод, Сеть. Межпроцессное взаимодействие
alexei
Уважаемые знатоки, есть вопрос.

На стороне сервера на linux у меня запущена программа, которые принимает соединения. Цикл работы следующий:
1) Получаем запрос на соединений.
2) Отправляем sizeof( int ) данных - размер в байтах идущего сообщения.
3) Отправляем сами данные

Раньше был linux-клиент и все работало (читал sizeof( int ) данных, потом определенное число данных из сокета). Решил сделать клиента кроссплатформенным, пытаясь считать данные следующим путем:

QTcpSocket::read( (long long int) sizeof( int ) ); // long long int = qint64

Получаю в качестве числа несуразную величину(да и вообще когда приходят данные от сервера их вывод никоим образом не напоминает число в начале, текст в конце то что надо - а вот число, никак не знаю как правильно передавать или считывать). Чем воспользоваться? По другому передавать число или считывать?

С уважением,
Алексей.
ViGOur
1. sizeof(int) в 32 и 64 битных системах отличаются.
2. непонятно как ты записываешь размер данных, как текст или записываешь его в первые 4 или 8 байта?

Покажи код отправки.
alexei
Цитата(ViGOur @ 25.3.2010, 20:14) *
1. sizeof(int) в 32 и 64 битных системах отличаются.
2. непонятно как ты записываешь размер данных, как текст или записываешь его в первые 4 или 8 байта?

Покажи код отправки.


1) Мне казалось по стандарту int - 4 байта везде.
2) Вот код для отправки (сначала отправляем размер данных потом сами данные):

    nbytes = write( fd, &n, sizeof( int ) );
    if( nbytes < 0 ) {
        fprintf( stderr, "Write: error writing to %d(fd) %d(int)\n", fd, n );
        return -1;
    }

    nbytes = write( fd, data, sizeof( char ) * n );
    if( nbytes < 0 ) {
        fprintf( stderr, "Write: error writing to %d(fd) %s(data)\n", fd, data );
        return -2;
    }
Litkevich Yuriy
Цитата(Гость_alexei_* @ 26.3.2010, 4:16) *
по стандарту int - 4 байта везде.
по стандарту int зависит от реализации. Гарантированную ширину имеют только его модификации с префиксом short и long, например: short int и long int

если тебе нужна гарантия ширины, то используй типы Qt:
qint32
qint64
и д.р.

Цитата(Гость_alexei_* @ 26.3.2010, 4:16) *
fd
что это такое у тебя в коде? какому типу данных соответсвует? И что за функция write, от какого класса?
alexei
Цитата(Litkevich Yuriy @ 26.3.2010, 2:03) *
Цитата(Гость_alexei_* @ 26.3.2010, 4:16) *
по стандарту int - 4 байта везде.
по стандарту int зависит от реализации. Гарантированную ширину имеют только его модификации с префиксом short и long, например: short int и long int

если тебе нужна гарантия ширины, то используй типы Qt:
qint32
qint64
и д.р.

Цитата(Гость_alexei_* @ 26.3.2010, 4:16) *
fd
что это такое у тебя в коде? какому типу данных соответсвует? И что за функция write, от какого класса?


Серверная часть написана из соображений минимальности использования внешний библиотек (установки Qt например), поэтому там нет таких понятий как qint64. fd - файловый дескриптор сокета, в который пишем данные (тип int). Серверную часть трогать не хочется - потому что она рабочая и общается с другими сущностями по такому искусственному протоколу (если менять сервер, то менять и все остальное). Простой вывод в консоль qt-клиента вырисовывает стрелочку влево (что соответствует в таблице ascii - 28, число которое я передавал со стороны сервера). Можно ли на qt-клиенте правильно обработать эту информацию?
alexei
Проверил, на стороне сервера и qt-клиента размеры данных одинаковы:

size of int = 4
size of short int = 2
size of long int = 4

правда число которое я получаю посредством sscanf: 156274600 (в двоичной системе даже на смешение байтов от 28 не похоже)
Litkevich Yuriy
alexei, я тебе предлагаю воспользоваться на стороне клиента функцией
QByteArray QIODevice::readAll ()
А затем преобразовать в HEX, например:

QTcpSocket socket;
QByteArray array;
array = socet.readAll();
qDebug() << "READ:" << array.toHex().toUpper();
Так ты увидишь, что ты принял, сразу в шестнадцатиричной форме.

П.С. не забывай, что пакет может фрагментироваться
alexei
Цитата(Litkevich Yuriy @ 26.3.2010, 23:50) *
alexei, я тебе предлагаю воспользоваться на стороне клиента функцией
QByteArray QIODevice::readAll ()
А затем преобразовать в HEX, например:

QTcpSocket socket;
QByteArray array;
array = socet.readAll();
qDebug() << "READ:" << array.toHex().toUpper();
Так ты увидишь, что ты принял, сразу в шестнадцатиричной форме.

П.С. не забывай, что пакет может фрагментироваться


Привожу куски кода qt-клиента (каким образом происходит чтение, не понял зачем мне смотреть шестнадцатиричная форму, когда я смотрю текст в обычном для чтения представлении):

QByteArray responseArr; // элемент класса

void CClientGui::slotReadyRead() {
    int err;
    int size = 0;
    char *buf = NULL;
    int percent;
    int idTask;
    static bool flagFirst = true;

    QDomDocument loc_xmlDoc;
    QDomElement loc_xmlRootElement;

    int bytes = CClientGui::tcpSocket.bytesAvailable();
    fprintf( stdout, "available %d bytes\n", (int)bytes );
    /*
     * First message structure :
     * <answer>
     *        <id>#id</id>
     * </answer>
     */  
    if( flagFirst == true ) {        
        responseArr.append( CClientGui::tcpSocket.readAll() );
        err = readFromServer( responseArr, size, &buf );
        if( err != 0 ) {
            fprintf( stderr, "Error read from socket" );
            return;
        }
//....


int CClientGui::readFromServer( QByteArray &bufAll, int& _size, char **_buf ) {
    char *buf = NULL;
    int size;

    fprintf( stdout, "allBuf: %s\n", bufAll.data() );
    fprintf( stdout, "allBuf hex: %s\n", bufAll.toHex().data() ); // добавил вывод в 16-ричной форме

    buf = new char[sizeof(int)];
    buf = bufAll.left( sizeof( int ) ).data(); // здесь должно быть чтение байт (никакого преобразования к qint64 и тому подобным)
    fprintf( stdout, "input read: %s\n", buf );
    _size += sizeof( int );
    sscanf( buf, "%d", &size );
    fprintf( stdout, "to read: %d bytes\n", size );
    bufAll.remove(0,sizeof(int));
    buf = new char[size+1];
    strcpy( buf, bufAll.right( size ).data() );
    buf[size] = '\0';
    *_buf = buf;
    buf = NULL;
    bufAll.remove( 0, size );
    _size += size;

    return 0;
}

alexei
Спасибо за совет с hex. Действительно байты перемешались, надо правильно инициализировать соединение. Будем разбираться
ViGOur
Попробуй читать с помощью qint64 QIODevice::read ( char * data, qint64 maxSize ), тоесть у тебя получится что-то вроде:
qint64 nRet=CClientGui::tcpSocket.read( &n, sizeof(int));
alexei
Цитата(ViGOur @ 27.3.2010, 14:19) *
Попробуй читать с помощью qint64 QIODevice::read ( char * data, qint64 maxSize ), тоесть у тебя получится что-то вроде:
qint64 nRet=CClientGui::tcpSocket.read( &n, sizeof(int));


1) Такой код не скомпилировался.
2) Я когда пишу с сервера - сделал преобразование числа посредством htonl, байты пришли в нужном порядке (в сетевом порядке - на стороне qt-клиента не должно быть преобразование на таком низком уровне ручками я полагаю), осталось их правильно обработать:
bufAll - содержит данные сокета (считанные посредством readAll() )
    buf = new char[sizeof(int)];
    strcpy( buf, bufAll.left( sizeof( int ) ).data() );
    fprintf( stdout, "input read: %s\n", buf );
    fprintf( stdout, "input read (hex): %s\n", bufAll.left( sizeof( int ) ).toHex().data() ); // здесь идет вывод - 0000001b ( = 27 )
    _size += sizeof( int );
    sscanf( buf, "%d", &size );
    fprintf( stdout, "to read: %d bytes\n", size ); // а здесь показывается что size = 2285116
ViGOur
Посмоти какое в твоем случае будет значение nSize:
int nSize=0;
memcpy( &nSize, bufAll.left( sizeof( int )).data(), sizeof( int));
код написан так же от руки и может не скомпилится, поправь его, bufAll.left( sizeof( int )) должен возвращать char *
alexei
Цитата(ViGOur @ 27.3.2010, 15:54) *
Посмоти какое в твоем случае будет значение nSize:
int nSize=0;
memcpy( &nSize, bufAll.left( sizeof( int )).data(), sizeof( int));
код написан так же от руки и может не скомпилится, поправь его, bufAll.left( sizeof( int )) должен возвращать char *


В этом случае: nSize = 452984832.
ViGOur
А если попробовать toAcii или toLocal8bit?
alexei
Цитата(ViGOur @ 27.3.2010, 16:29) *
А если попробовать toAcii или toLocal8bit?


Не понял к чему применять и зачем? У меня уже есть вывод в 16-ричном виде:
fprintf( stdout, "input read (hex): %s\n", bufAll.left( sizeof( int ) ).toHex().data() ); // здесь идет вывод - 0000001b ( = 27 )


Вопрос как из этого буфера уже правильно считать число?
Litkevich Yuriy
alexei, проблема в следующем: особо вникать в чужую проблему нет ни времени ни желания.
Если б ты привел подробную информацию об отправке и о приёме, то быстрее было бы найдено решение.
Напиши например так:
Отправляю:
Формат | Длинна | Сообщение
естест. | число 27 | "hello"
HEX | 1B | тоже в HEX'е

Принимаю в HEX:
00 00 1B 44 A9 ...
Вот если так опишешь, то может и сам поймёшь проблему
ViGOur
Если у тебя данные выглядят как 0000001b, то при memcpy в nSize должно записаться число 27. :)
Иначе используй метод read он аналогичен твоему на других клиентах...
alexei
вот код отправки:
int  writeToClient (int fd, int n, char *data ) {
    int  nbytes;
    long int hton_n = htonl( n );

    CLog::SLogDate( "commFunc", "%d", n );
    nbytes = write( fd, &hton_n, sizeof( int ) );
    CLog::SLogDate( "commFunc", "%ld", hton_n );
    if( nbytes < 0 ) {
        fprintf( stderr, "Write: error writing to %d(fd) %d(int)\n", fd, n );
        return -1;
    }
    if (nbytes > 0) {
        #ifdef _SENDTHREAD
        printf("percent out\n");
        #endif
    }

    nbytes = write( fd, data, sizeof( char ) * n );
    if( nbytes < 0 ) {
        fprintf( stderr, "Write: error writing to %d(fd) %s(data)\n", fd, data );
        return -2;
    }

    CLog::LogDate( "commFunc", data );
    return 0;
}


Что отправляется:
Sat Mar 27 17:57:12 2010 commFunc 27
Sat Mar 27 17:57:12 2010 commFunc 452984832
Sat Mar 27 17:57:12 2010 commFunc <answer><id>0</id></answer>

Т.е. размер данных 27 байт, это число мы представили в стандартном для передачи по сети виде (htonl) - 452984832. Следующим пакетом будем пересылать "<answer><id>0</id></answer>" - 27 байт. Итого уехало клиенту - 31.

Что происходит на стороне клиента:
    int bytes = CClientGui::tcpSocket.bytesAvailable();
    fprintf( stdout, "available %d bytes\n", (int)bytes );
    fprintf( stdout, "before read buf: %s\n", responseArr.data() );
    responseArr.append( CClientGui::tcpSocket.readAll() );

// вызываем функцию (здесь responseArr = bufAll)

    strcpy( buf, bufAll.left( sizeof( int ) ).data() );
    fprintf( stdout, "input read: %s\n", buf );
    fprintf( stdout, "input read (hex): %s\n", bufAll.left( sizeof( int ) ).toHex().data() );


Что приехало:
available 31 bytes
before read buf: //здесь пусто
input read: //здесь пусто
input read (hex): 00 00 00 1b 3c 61 6e 73 77 65 72 3e 3c 69 64 3e 30 3c 2f 69 64 3e 3c 2f 61 6e 73 77 65 72 3e

"3c 61 6e 73 77 65 72 3e 3c 69 64 3e 30 3c 2f 69 64 3e 3c 2f 61 6e 73 77 65 72 3e" = "<answer><id>0</id></answer>" (проверить можно здесь: hex to string)

осталось разобраться с 00 00 00 1b

делаем так:
memcpy( &nSize, bufAll.left( sizeof( int )).data(), sizeof( int ) );


действительно nSize = 452984832
Даже htonl не надо делать, пока все писал, вот и разобрался.

Отдельное спасибо Litkevich Yuriy (memcpy выручило).
ViGOur
Ну вот, а то уже начал думать, что я совсем плохой стал... :)
alexei
Цитата(ViGOur @ 27.3.2010, 18:18) *
Если у тебя данные выглядят как 0000001b, то при memcpy в nSize должно записаться число 27. :)
Иначе используй метод read он аналогичен твоему на других клиентах...


у меня данные пришли такого рода 1b000000. Прочиталось 27 все отлично, но беспокоит один вопрос - ручками не придется делать htonl на других платформах (если сходу кто ответи буду благодарен, если нет, путем ждать - когда у кого то возникнут проблемы =) )?

Еще просьба к администраторам.
Поскольку я не являюсь автором этой темы (открывал, когда не был зарегистрирован), не могли бы Вы проставить в названии "[ЗАКРЫТО]" ?
Litkevich Yuriy
Цитата(alexei @ 27.3.2010, 21:36) *
Отдельное спасибо Litkevich Yuriy (memcpy выручило).
memcpy - идея ViGOur'а, а не моя :)
alexei
Цитата(Litkevich Yuriy @ 27.3.2010, 19:18) *
Цитата(alexei @ 27.3.2010, 21:36) *
Отдельное спасибо Litkevich Yuriy (memcpy выручило).
memcpy - идея ViGOur'а, а не моя :)


Собственно в репутации я написал правильное спасибо за memcpy =) а в сообщение неправильно скопировал имя, простите :rolleyes:
ViGOur
Цитата(alexei @ 27.3.2010, 19:10) *
Поскольку я не являюсь автором этой темы (открывал, когда не был зарегистрирован), не могли бы Вы проставить в названии "[ЗАКРЫТО]" ?
В названии темы не обязательно. У автора темы есть возможность закрыть тему, ну и сменить ее название, в данном случае добавить к началу темы решено...

Сделал.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.