Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. [SOLVED] QDataStream and QTcpSocket problem in multi-threaded application
QtWS25 Last Chance

[SOLVED] QDataStream and QTcpSocket problem in multi-threaded application

Scheduled Pinned Locked Moved General and Desktop
12 Posts 3 Posters 13.4k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • M Offline
    M Offline
    mecenas1991
    wrote on last edited by
    #1

    Hi,

    I'm a beginner programmer and I'm writing small client - server application (something like IRC) and i started to modify server which was not fully working. I found a problem with sending/receiving data through QTcpSocket (it previously worked but wasn't in separate threads).

    As shown in part of code reading from QDataStream on QTcpSocket device works but writing to this stream - not. I test it on working client and there were no problems with old server.

    I have tried to move the code from second thread to first and using only one thread but the problem still existed.

    QTcpSocket::isOpen(), QTcpSocket::isReadable(), QTcpSocket::isValid() and QTcpSocket::isWritable() returns true. There is no error after writing to stream. Everything seems to be working but client application does not receive any data.

    Here is part of code:

    @
    //class ListenerThread extends QThread
    //class LoginThread extends QThread

    void ListenerThread::run(){
    this->serverSocket = new QTcpServer();
    if(!(serverSocket->listen(hostAddr,port))){
    return;
    }
    while(true){
    if(!(serverSocket->hasPendingConnections())){
    serverSocket->waitForNewConnection(-1);
    }
    (new LoginThread(serverSocket->nextPendingConnection()->socketDescriptor()))->start();
    }
    }

    LoginThread::LoginThread(int sockDsc, QObject *parent) :
    QThread(parent)
    {
    this->sockDsc = sockDsc;
    }

    void LoginThread::run(){
    QTcpSocket* socket = new QTcpSocket();
    socket->setSocketDescriptor(this->sockDsc);
    QDataStream clientStream(socket);
    QString data = QString();
    clientStream>>data; //it works
    clientStream<<QString("-10"); //and it doesn't
    }
    @

    I also tried to send more data to be sure that there is no problem with the short time between read and write.
    Can anyone help me to solve this?

    1 Reply Last reply
    0
    • L Offline
      L Offline
      lgeyer
      wrote on last edited by
      #2

      Have you tried stepping into #29 to find out if there is any obvious reason why the data isn't sent; have you tried sniffing the network traffic to make sure the data isn't sent?

      Have you tried just sending data, not receiving?

      A few annotations:

      • You do not need a secondary thread; QTcpServer already works asynchronously (as long as you have a spinning event loop) and emits the newConnection() signal each time a client connects
      • You are leaking memory, as <code>this->serverSocket</code> is neither deleted explicitily nor implicitly (as it has no parent set); the same goes for <code>socket</code> in <code>LoginThread</code>
      • You should delete the QTcpSocket returned from nextPendingConnection(); it is implicitly deleted with the server, but until the server is deleted QTcpSocket objects will stack and waste memory
      • I assume you take the detour by the socket descriptior to cross thread boundaries. I recommend using incomingConnection() then, as it prevents you from creating a temporary QTcpSocket object in the first place
      • And, as always when it comes to threading in Qt make sure you've read "Threads, Events and QObjects":http://qt-project.org/wiki/Threads_Events_QObjects, "QThreads General Usage":http://qt-project.org/wiki/QThreads_general_usage and "You’re Doing It Wrong":http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
      1 Reply Last reply
      0
      • M Offline
        M Offline
        mecenas1991
        wrote on last edited by
        #3

        ListenerThread now is Listener and inherits from QObject, LoginThread is removed and all operations from LoginThread::run() are moved to Listener::login() slot. Reading and writing to stream works (client receives data correctly) but it's only part of code and there is one small question. I decided to create threads to be able to handle multiple login requests simultaneously, eg. 3 clients are trying to log in but have to wait in queue. As i think every signal being sent in this case is executed in the same thread what makes it unable to handle multiple requests at the same time.

        @
        void Listener::Init(){
        QObject::connect(this,SIGNAL(newConnection()),this,SLOT(login()));
        if(!(listen(hostAddr,port))){
        SharedData::error(errorString());
        }
        }

        void Listener::login(){

        //some login procedures

        }
        @

        After successful login every client (represented by it's own class) is moved to something what I called "Message Router" which handles transferring messages from client X to client Y.

        @
        void Listener::login(){

        //waiting for login data
        //reading login data from stream
        //checking if login data isn't empty
        //checking if server is full
        //checking client type (for future use)
        //checking client version
        //connecting to users database
        //checking if user exists
        //checking if password is correct

        SharedData::router->addClient(socketDescriptor,id,clientType);

        }
        @

        Message Router is also a thread and the socket which client is communicating by have to be moved to another thread, to prevent messages from waiting.

        How to make QTcpSocket working in thread which is not a thread where it was created? Should I reimplement QTcpServer::incomingConnection()? I didn't try sniffing yet but I'll check if find that stream sends data correctly. Debugger don't show any problems with writing to stream.

        This program can run without using threads and then the problem with QTcpSocket will be solved by using signals and slots but it appears to me that it doesn't ensure continuous work regardless of current client request.

        1 Reply Last reply
        0
        • L Offline
          L Offline
          lgeyer
          wrote on last edited by
          #4

          You can 'pass' a connection between threads the way you already did; by passing the socket descriptor and then creating a new socket in the new thread using this descriptor.

          Although nextPendingConnection() also works, I would use incomingConnection() instead, as it directly provides the socket descriptor and saves you from creating an (unneccessary) QTcpSocket object.

          I just want to mention that Qt also provides high level concurrency, which allows for executing stuff asynchronosly without having to mess with low level threads.
          @
          class Listener : public QTcpServer
          {
          Q_OBJECT

          public:
          Listener(QObject *parent = 0) : QTcpServer(parent)
          {
          ...
          }

          protected:
          void incomingConnection(int handle)
          {
          // execute login in a different thread (taken from global thread pool)
          QtConcurrent::run(this, &Listener::login, handle);
          }

          private:
          void login(int handle)
          {
          QTcpSocket socket;
          socket.setSocketDescriptor(handle);
          ...
          }
          };
          @
          Brain to terminal.

          1 Reply Last reply
          0
          • M Offline
            M Offline
            mecenas1991
            wrote on last edited by
            #5

            I used your solution and everything works fine except the QTcpSocket... As i posted before reading works and writing does not. I compared Wireshark results for old server and this code. There's no ACK packet with data coming from "new server". I don't know what may cause this problem.

            Here's my code:

            main.cpp

            @
            #include <QtCore/QCoreApplication>
            #include <iostream>

            #include "listener.h"
            #include "msgrouterthread.h"
            #include "shareddata.h"

            using namespace std;

            int main(int argc, char *argv[])
            {
            QCoreApplication a(argc, argv);

            if(SharedData::Init()){
                cout<<"Unable to initialize server data"<<endl;
                return 1;
            }
            
            Listener listener;
            listener.Init();
            SharedData::router->start();
            cout<<"Server started successfully"<<endl;
            
            return a.exec&#40;&#41;;
            

            }
            @

            listener.h

            @
            #ifndef LISTENER_H
            #define LISTENER_H

            #include <QtCore>
            #include <QTcpServer>

            #include "clienttypes.h"
            #include "errorcodes.h"
            #include "shareddata.h"

            class Listener : public QTcpServer
            {
            Q_OBJECT
            public:
            explicit Listener(QObject *parent = 0);
            void Init();

            protected:
            void incomingConnection(int);

            private:
            void login(int);

            };

            #endif // LISTENER_H
            @

            listener.cpp (shortened)

            @
            #include "listener.h"

            Listener::Listener(QObject *parent) :
            QTcpServer(parent)
            {
            }

            void Listener::Init(){
            if(!(listen(SharedData::hostAddr,SharedData::port))){
            SharedData::error(this->errorString());
            return;
            }
            }

            void Listener::incomingConnection(int handle){
            QtConcurrent::run(this, &Listener::login, handle);
            }

            void Listener::login(int handle){
            std::cout<<"Login requested"<<std::endl;

            //login
            
            QTcpSocket socket;
            socket.setSocketDescriptor(handle);
            
            if(!(socket.bytesAvailable())){
                if(!(socket.waitForReadyRead())){
                    std::cout<<"Connection timed out"<<std::endl;
                    socket.disconnectFromHost();
                    return;
                }
            }
            
            QDataStream clientStream(&socket);
            QString data = QString();
            clientStream>>data;   //works fine
            
            
            // ... some code ...
            
            
            if(userPwd != passwd){
                std::cout<<"Incorrect password"<<std::endl;
                QString errMsg(INCORRECT_PASSWORD);
                errMsg.append('\n');
                clientStream<<errMsg;  //doesn't work
                socket.disconnectFromHost();
            

            // ... some code ...

             return;
            }
            

            // ... some code ...

            }
            @

            I also tried to send some data immediately after reading from stream, but it doesn't change anything.

            @
            QDataStream clientStream(&socket);
            QString data = QString();
            clientStream>>data; //works fine
            clientStream<<"some data which won't be received by client";
            clientStream<<QString("other data with the same problem");
            @

            QTcpSocket::write() doesn't work too.

            Screenshots from Wireshark:

            From old server (packet with data)
            "old":http://bronexproduction.pl/shared/old.png

            From new server (no data)
            "new":http://bronexproduction.pl/shared/new.png

            1 Reply Last reply
            0
            • F Offline
              F Offline
              franku
              wrote on last edited by
              #6

              Use socket.flush() after writing to the stream or using write() directly -- it seems that the socket is not being sent because there is no event loop that does the work for you, since the socket uses buffered I/O.

              This, Jen, is the internet.

              1 Reply Last reply
              0
              • M Offline
                M Offline
                mecenas1991
                wrote on last edited by
                #7

                I'm glad to say that socket transfer works :) thank you very much. But I have one more question about socket descriptors relative to this code:

                @
                void Listener::incomingConnection(int handle){
                QtConcurrent::run(this, &Listener::login, handle);
                }

                void Listener::login(int handle){
                std::cout<<"Login requested"<<std::endl;

                QTcpSocket socket;
                socket.setSocketDescriptor(handle);
                
                // ... some code ...
                
                emit addClient(handle,id,clientType);
                

                }
                @

                I pass the descriptor by emitting Listener::addClient signal and when the function ends this socket
                @
                QTcpSocket socket;
                @
                is deleted and the file descriptor is closed so I can't use it in next function. Creating this socket by using a pointer and not deleting it might be not so good. Is there any way to keep descriptor opened when deleting QTcpSocket object?

                1 Reply Last reply
                0
                • F Offline
                  F Offline
                  franku
                  wrote on last edited by
                  #8

                  For what reason?

                  This, Jen, is the internet.

                  1 Reply Last reply
                  0
                  • M Offline
                    M Offline
                    mecenas1991
                    wrote on last edited by
                    #9

                    I'd like to be able to use this connection in different thread, here's a sample code

                    @
                    void MsgRouterThread::addClientSlot(int handler, QString ID, qint64 clientType){
                    QtConcurrent::run(this,&MsgRouterThread::addClient,handler,ID,clientType);
                    }

                    void MsgRouterThread::addClient(int handler, QString ID, qint64 clientType){

                    Client client(handler,ID,clientType);
                    clientByID.insert(ID,Client(handler,ID,clientType));
                    
                    QTcpSocket socket;
                    socket.setSocketDescriptor(handler);
                    
                    QDataStream stream(&socket);
                    
                    stream<<QString(LOGIN_SUCCESSFUL);
                    socket.flush();
                    
                    // ... some code ...
                    

                    }

                    // ... some other methods ...
                    @

                    I can't use here the descriptor unless socket from Listener::login() exists

                    1 Reply Last reply
                    0
                    • F Offline
                      F Offline
                      franku
                      wrote on last edited by
                      #10

                      The tcp socket destructor will close the connection, see here: "QTcpSocket":http://qt-project.org/doc/qt-4.8/qtcpsocket.html#dtor.QTcpSocket.

                      Creating a tcp socket on the heap won't help because you cannot use the same socket in more than one thread. Why don't you use the same thread for all work?

                      This, Jen, is the internet.

                      1 Reply Last reply
                      0
                      • M Offline
                        M Offline
                        mecenas1991
                        wrote on last edited by
                        #11

                        Finally I used one thread for QTcpSocket, as you suggested. I had to change my plans a bit for that reason. Everything uses signal/slot communication and didn't crash yet. I hope it won't suprise me too much.

                        Thanks a lot

                        1 Reply Last reply
                        0
                        • F Offline
                          F Offline
                          franku
                          wrote on last edited by
                          #12

                          You're welcome. If you want to mark this thread [SOLVED] everyone will be happy.

                          This, Jen, is the internet.

                          1 Reply Last reply
                          0

                          • Login

                          • Login or register to search.
                          • First post
                            Last post
                          0
                          • Categories
                          • Recent
                          • Tags
                          • Popular
                          • Users
                          • Groups
                          • Search
                          • Get Qt Extensions
                          • Unsolved