Russian Qt Forum

Qt => Работа с сетью => Тема начата: Alhin от Октябрь 31, 2009, 11:33



Название: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: Alhin от Октябрь 31, 2009, 11:33
Стал реализовывать клиент и сервер обменивающиеся сообщениями по tcp, когда клиент подключается к серверу,  для него запускается отдельный поток. Так вот, если клиент шлет сообщения серверу, то все нормально, но после того как сервер посылает сообщение клиенту, клиентские сообщения до сервера больше не доходят, соответственно код клиента и сервера прилагается.

Код класса потока обрабатывающего прием и отправку сообщений на серверной стороне:
Код:
void ClientThread::readClientMessage(){
    this->__bufferForInputData += this->__clientSocket->readAll();
    forever{
        QDataStream in(&__bufferForInputData, QIODevice::ReadOnly/*this->__clientSocket*/);
        in.setVersion(QDataStream::Qt_4_5);
        if(this->__nextBlockSize == 0){
            if(this->__bufferForInputData.size() < sizeof(qint64)){
                return;
            }
            in >> this->__nextBlockSize;
        }
        if(this->__bufferForInputData.size() < sizeof(qint64) + this->__nextBlockSize){
            return;
        }
        QString message;   
        in >> message;
        this->__nextBlockSize = 0;
        emit gottenMessage(message);
        this->__bufferForInputData.clear();
        return;
    }
}

void ClientThread::sendMessage(QString message){
    QByteArray data;
    QDataStream out(&data, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_5);
    out << (qint64)0;
    out << message;
    out.device()->seek(0);
    out << (qint64)(data.size() - sizeof(qint64));

 
    this->__clientSocket->write(data, data.size());
    this->__clientSocket->waitForBytesWritten();
}

На стороне клиента прием и отправка сообщений реализованы точно также:
Код:
void TcpClient::sendMessage(QString message){
    if(this->__tcpSocket->state() != QAbstractSocket::ConnectedState){
        return;
    }
    QByteArray data;
    QDataStream out(&data, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_5);
    out << (qint64)0;
    out << message;
    out.device()->seek(0);
    out << (qint64)(data.size() - sizeof(qint64));


    this->__tcpSocket->write(data, data.size());
    this->__tcpSocket->waitForBytesWritten();
}

void TcpClient::readServerMessage(){
    this->__bufferForInputData += this->__tcpSocket->readAll();
    forever{
        QString message;
        QDataStream in(&__bufferForInputData, QIODevice::ReadOnly/*this->__tcpSocket*/);
        if(this->__nextBlockSize == 0){
            if(this->__bufferForInputData.size() < sizeof(qint64)){
                return;
            }
            in >> this->__nextBlockSize;
        }
        if(this->__bufferForInputData.size() < sizeof(qint64) + this->__nextBlockSize){
            return;
        }
        in >> message;
        this->__nextBlockSize = 0;
        emit gottenMessage(message);
        this->__bufferForInputData.clear();
        return;
    }
}

Есть идеи как решить эту проблему?
Заранее спасибо!


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: BRE от Октябрь 31, 2009, 11:51
Смущает следующий кусок кода в обоих слотах чтения:
Код
C++ (Qt)
void ClientThread::readClientMessage(){
...
       if(this->__bufferForInputData.size() < sizeof(qint64) + this->__nextBlockSize){
           return;
       }
...
}
 
Размер блока (sizeof(qint64)) ты уже прочитал.

Попробуй так:
Код
C++ (Qt)
void ClientThread::readClientMessage(){
...
       if(this->__bufferForInputData.size() < this->__nextBlockSize){
           return;
       }
...
}
 


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: Alhin от Октябрь 31, 2009, 12:02
Исправил, но проблема все равно осталась.


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: BRE от Октябрь 31, 2009, 12:08
Исправил, но проблема все равно осталась.
И в TcpClient::readServerMessage() исправил?

Вопрос по протоколу:
* Клиент подключается к серверу
* Клиент посылает сообщение серверу (нормально доходит?)
* Сервер посылает сообщение клиенту (нормально доходит?)
* Клиент посылает сообщение серверу (сервер его не получает?)


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: Alhin от Октябрь 31, 2009, 20:10
Да, там тоже исправил.
Клиент подключается к серверу - да.
Клиент посылает сообщение серверу - все доходит без проблем.
Сервер посылает сообщение клиенту - тоже все доходит и проблем не наблюдается.
(*) Клиент посылает сообщение серверу - здесь уже сервер не получает сообщений.
Причем заметил такую интересную вещь, что после того как серверу не дошло сообщение от клиента
переслать от сервера клиенту сообщение, то серверу приходит отправленное в случае (*) сообщение(
причем только первое из отправленных). Если же этого не делать, то сообщения не поступают.

Причем, если закоментить в коде клиента и сервера:
Код:
this->__tcpSocket->waitForBytesWritten();
то такого интересного эффекта не наблюдается.

Решил проверить свой код на debian - все работает без проблем.
В голове не укладывается в чем тут может быть дело...


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: BRE от Октябрь 31, 2009, 20:15
Причем, если закоментить в коде клиента и сервера:
Код:
this->__tcpSocket->waitForBytesWritten();
то такого интересного эффекта не наблюдается.
А для чего ты вызываешь эту функцию?


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: Alhin от Октябрь 31, 2009, 20:29
На самом деле первоначальный вариант был без нее,
дописал ее из соображения "авось поможет".


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: BRE от Октябрь 31, 2009, 20:30
Так сейчас под linux все работает, а под вендой нет?


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: Alhin от Октябрь 31, 2009, 20:31
Да, именно так.


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: BRE от Октябрь 31, 2009, 20:36
А попробуй вместо
Код
C++ (Qt)
this->__tcpSocket->waitForBytesWritten();
 
поставить
Код
C++ (Qt)
this->__tcpSocket->flush();
 


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: Alhin от Октябрь 31, 2009, 20:39
Пробовал - не помогает.


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: BRE от Октябрь 31, 2009, 20:46
Пробовал - не помогает.
Хм, странно... С вендой помочь не смогу, не пользуюсь, но если захочешь выложи архив с проектом, я его под linux посмотрю, кто-то может под вендой попробует.


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: Alhin от Октябрь 31, 2009, 21:00
Проект смогу выложить только утром в понедельник.


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: Alhin от Ноябрь 02, 2009, 10:08
Вот обещанный архив с исходниками, соответственно исходники клиента в папке Client, а сервера - Server.


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: BRE от Ноябрь 02, 2009, 12:55
Вот обещанный архив с исходниками, соответственно исходники клиента в папке Client, а сервера - Server.
Лучше и pro файлы в архив класть.  ;)

Сейчас нет времени подробно разбираться... Посмотри на сообщения, которые выдаются в консоль при работе сервера.

Если объект создается в конструкторе Thread, то он создается и принадлежит контексту того потока из которой вызывается конструктор (в твоем случае это главный поток), а используешь ты его в контексте дочернего потока (в методе run). Нужно или переносить объект в контекст дочерней нити (см QObject::moveToThread), или непосредственно создавать объект в контексте дочерней нити (т.е. в методе run).


Название: Re: Непонятная проблема с передачей сообщен&#
Отправлено: Alhin от Ноябрь 04, 2009, 17:42
Я, наконец то, нашел решение проблемы, нужно было QTcpSocket для клиентского потока создавать на стороне сервера, а не в клиентском потоке. Как только я так сделал, то все заработало нормально.

BRE, спасибо большое за помощь!


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: niXman от Декабрь 06, 2009, 04:08
Цитировать
Я, наконец то, нашел решение проблемы, нужно было QTcpSocket для клиентского потока создавать на стороне сервера, а не в клиентском потоке.
Поясните пожалуйста. Любопытно ;)


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: VisJiser от Декабрь 22, 2009, 09:05
выложите пожалуйста исправленный пример


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: VisJiser от Декабрь 22, 2009, 15:48
мде..несколько дней убил.
если кому то интересно что за трабла и решение, то стоит почитать ветку http://forum.vingrad.ru/act-ST/f-466/t-267120.html#st_15_view_0
зы: по моему Alhin сделал из многопоточного сервера однопоточный, если я правильно понял его объяснения. впрочем из меня программер никакой, так что на авторитетность не заявляю


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: Alhin от Январь 22, 2010, 22:37
Вы не правы, сервер прекрасно работает со многими клиентами, каждый из которых обслуживается в отдельном потоке.


Название: Re: Непонятная проблема с передачей сообщений по tcp между клиентом и сервером
Отправлено: Alhin от Январь 22, 2010, 22:55
По поводу пояснения:
Цитировать
Я, наконец то, нашел решение проблемы, нужно было QTcpSocket для клиентского потока создавать на стороне сервера, а не в клиентском потоке.

Для того что бы решить проблему, которая у меня возникла в классе сервера, наследнике QTcpServer, надо переопределить процедуру
Код:
virtual void incomingConnection ( int socketDescriptor )
в которой написать, что то вроде:

Код:
 
void incomingConnection ( int socketDescriptor ){
    QTcpSocket *clientSocket = new QTcpSocket(this);
    clientSocket->setSocketDescriptor(socketDescriptor);
    ClientThread *thread = new ClientThread(clientSocket, this);
    ...
}

Где ClientThread - класс потока(наследник QThread), в котором обрабатывается каждое клиентское соединение. ClientThread имеет конструктор:

Код:
ClientThread::ClientThread(QTcpSocket *socket, QObject = 0){
   ...
}