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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Как работает QTcpSocket::write()?  (Прочитано 15132 раз)
ilyagoo
Гость
« : Ноябрь 20, 2009, 12:02 »

вопрос в том, когда сокет действительно отдает данные TCP/IP протоколу?
делаю так:
1) пишу в сокет заголовок пакета:
Код:
struct TPackageHeader
{
   int PackageNumber;
   int PackageSenderID;
   int PackageDataSize;
};
2) пишу данные, их размер указан в TPackageHeader::PackageDataSize.
Код:
void f()
{
   TPackage header;
   header.PackageDataSize = 1024;
   socket->write( &header, sizeof( header ) );
   socket->write( data, header.PackageDataSize );
}
На противоположном конце шланга другой сокет читает заголовок, читает данные, основываясь на TPackageHeader::PackageDataSize.
(*)Ожидается, что после вызова f() процесс попадет в цикл обработки событий и данные уйдут по сети.
Значит, если я буду делать
Код:
for ( int i = 0; i < 100; i++ )
   f();
Через шланг может политься инфа, разбитая на пакеты произвольно, но в результате я получаю просто 3-4 пакета, при повторном вызове новае пакеты дают пинок предыдущим, и те приходят еще одной кучкой из 3х-4х. Куда при этом деваются остальные - неясно.

Если предположить, что (*) верно, хотя уже предыдущий пример заставляет усомниться в этом, то положение могло бы исправить следующее действие:
Код:
for ( int i = 0; i < 100; i++ )
{
   f();
   qApp->processEvents();
}
Но, увы...
В чем тут фишка? Причем, начинает доходить около 60% пакетов, а со стороны отправителя фиксируется отправка всех пакетов в любом случае.
« Последнее редактирование: Ноябрь 20, 2009, 12:04 от ilyagoo » Записан
BRE
Гость
« Ответ #1 : Ноябрь 20, 2009, 12:10 »

Попробуй после write делать flush.

Почитай про TCP и в частности про алгоритм Nagle, который старается оптимизировать число исходящих пакетов.
Записан
ilyagoo
Гость
« Ответ #2 : Ноябрь 20, 2009, 12:15 »

Цитировать
Попробуй после write делать flush.
не помогает

читаю о nagle...
прочел:
Цитировать
Applications that expect real time responses can react poorly with Nagle's algorithm. Applications such as networked multiplayer video games expect that actions in the game are sent immediately, while the algorithm purposefully delays transmission, increasing bandwidth at the expense of latency. For this reason applications with low-bandwidth time-sensitive transmissions typically use TCP_NODELAY to bypass the Nagle delay

TCP_NODELAY это где-то в системе?
« Последнее редактирование: Ноябрь 20, 2009, 12:21 от ilyagoo » Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #3 : Ноябрь 20, 2009, 14:27 »

Цитировать
не помогает
должно помочь
Записан

ArchLinux x86_64 / Win10 64 bit
BRE
Гость
« Ответ #4 : Ноябрь 20, 2009, 14:30 »

TCP_NODELAY это где-то в системе?
Ты бы еще систему озвучил.
Записан
ilyagoo
Гость
« Ответ #5 : Ноябрь 20, 2009, 14:59 »

WinXP, МСВС. flush() не помогает нигде.
пытаюсь через WinAPI setsockopt() - та же фигня...
Qt-4.4.2, версию сменить не могу, если что Улыбающийся

Записан
maxxant
Гость
« Ответ #6 : Ноябрь 20, 2009, 16:00 »


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


Лучше вообще не думать о низкоуровневой реализации протокола, TCP для программиста просто поток данных. Скорее всего ошибка в коде. Как читаете данные? Типичная ошибка бывает при чтении и неправильной обработке сигнала readyRead() - поскольку по приходу этого сигнала необходимо считывать все данные, а не свой один пакет, как иногда пытаются делать.

+ при такой структуре пакета неизвестна заранее его длина и для того чтобы пакет считать бывает нужно использовать QIODevice::peek(), чтобы без опустошения буфера проверить пришёл ли пакет целиком или нет (нужно считать PackageDataSize). Просто проще это делать когда первым полем идёт именно размер пакета.

+ socket->write( &header, sizeof( header ) );
не есть хорошо, sizeof - не кроссплатформенно, размер структуры может зависеть от выравнивания в опциях компилятора или процессора, также не учитывается порядок байт при такой записи в сокет - не критично если пишите для себя и только для одного процессора. В ином случае лучше писать по одному полю структуры в сокет и использовать явные размеры типов типа quint32, qint8, и т.д. Ещё хорошо бы осилить QByteArray и QDataStream и писать все пакеты как QByteArray (прим на всяк случай: с Qt 4.6. тип float будет в QDataStream всегда писаться с размером double).
Записан
ilyagoo
Гость
« Ответ #7 : Ноябрь 20, 2009, 16:16 »

Цитировать
не есть хорошо, sizeof - не кроссплатформенно, размер структуры может зависеть от выравнивания в опциях компилятора или процессора, также не учитывается порядок байт при такой записи в сокет
абсолютно согласен Улыбающийся

Цитировать
Ещё хорошо бы осилить QByteArray и QDataStream
я-то осилю, а вот осилит ли клиент, написанный не на Qt... видимо, нет.

а вот читаю данные я возможно левой задней... минут через 5 закину исходник
Записан
ilyagoo
Гость
« Ответ #8 : Ноябрь 20, 2009, 16:25 »

там контроля никакого, сорри, нет времени на баловство Улыбающийся
запускаешь два экземпляра ( можно и один, но отладка смешается ), на одном первом жмешь Start, на втором - Connect. С первого можно слать данные. IP, естесственно ставишь свой.
Записан
ilyagoo
Гость
« Ответ #9 : Ноябрь 20, 2009, 16:40 »

мда... левой задней - мягко сказано, я просто не дочитываю оставшееся, что бы с этим делать?
может перед выходом из функции сгенерить сигнал readyRead() самому?
Записан
maxxant
Гость
« Ответ #10 : Ноябрь 20, 2009, 16:54 »

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

в void ClientServer::receivePackage()  может как-то while() {} прикрутить проще.

вообще у меня типично как то так, с учётом что первое 32 битное поле это размер пакета а потом любые данные:
Код:
//------------------------------------------------------------------------------
void net::connection::io_dev_ready_read()  // от сигнала readyRead()
{
    while(checkMessage())
    {
        // msg ready for read
        qint32 _size=0;
        QByteArray data;
        qint64 _readed = p_ioDevice->read((char*) &_size, sizeof(_size));
        if((sizeof(_size)==_readed) && (_size>0))
        {
            if(_size > const_message_size_max)
            {
                qCritical() << "big message received "
                        << _size << " bytes!";
                this->resetState();
                return;
            }
            data = p_ioDevice->read(_size);
            if(data.size() != _size)
            {
                qCritical() << "couldn't read full message";
                this->resetState();
                return;
            }
            emit evPacketReceived(data);
      }
   }
}

//------------------------------------------------------------------------------
bool net::connection::checkMessage()
{
    qint64 _bytes_available = p_ioDevice->bytesAvailable();
    if(_bytes_available<4) return false;

    qint32 _size=0;
    qint64 _peek_size = p_ioDevice->peek((char*)&_size, sizeof(_size));
    if(_peek_size != sizeof(_size)) return false;
    _bytes_available = p_ioDevice->bytesAvailable();
    if(_bytes_available < sizeof(_size) + _size) return false;

    return true;
}

сокет наследуется от QIODevice поэтому у меня  p_ioDevice  - по сути указатель на QTcpSocket.
« Последнее редактирование: Ноябрь 20, 2009, 16:58 от maxxant » Записан
ilyagoo
Гость
« Ответ #11 : Ноябрь 24, 2009, 13:54 »

перед выходом из
Код:
void ClientServer::receivePackage()
проверяю, еостались ли еще данные:
Код:
   if (m_Client->bytesAvailable()) emit rereadData();
этот сигнал связан с сигналом
Код:
QTcpSocket::readyRead()

сравнил в профайлере с вариантом io_dev_ready_read() - мой производительнее.

теперь все данные приходят Улыбающийся

но проблемы продолжаются: при попытке пересылать пакеты 50000 байтов (цифра случайная, а не граничная) на принимающем конце шланга данные еле-еле приходят, а отправитель толстеет в памяти на глазах. очевидно, отправитель ставит пакеты в очередь TCP\IP, вот только как это проверить, ведь QTcpSocket::write() возвращает правильное количество записанных байтов?
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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