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. QUdpSocket and QThread
Forum Updated to NodeBB v4.3 + New Features

QUdpSocket and QThread

Scheduled Pinned Locked Moved General and Desktop
7 Posts 3 Posters 9.0k Views 1 Watching
  • 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
    Mr.Orange
    wrote on last edited by
    #1

    Hey guys!

    I have a problem with a QUdpSocket running in a different thread.

    Here's the source code:

    @
    /*

    • main.cpp
    • Main app
    • (actually the code is in a constructor of a class, but I've shortened it a bit...)
    • That's where the RemoteConnection object is created and the thread is started.
      */
      int main()
      {
      remoteConnection = new RemoteConnection();
      remoteConnection->start();

    remoteConnection->setHostPort(1234); // ERROR!
    }

    /*

    • remoteconnection.h
    • Class RemoteConnection

    */
    class RemoteConnection : public QThread
    {
    Q_OBJECT

    private: QUdpSocket *udpSocket;

    public: RemoteConnection(ve::Scene *scene);

    public: void setHostPort(unsigned int hostPort);

    public slots: void readyRead();

    public: virtual void run();
    }

    /*

    • remoteconnection.cpp
    • Class RemoteConnection implementation

    */
    RemoteConnection::RemoteConnection()
    {
    this->remoteIP = "127.0.0.1";
    this->remotePort = 5000;
    this->hostPort = 5001;
    udpSocket = NULL;
    }

    void RemoteConnection::run()
    {
    // create the socket
    udpSocket = new QUdpSocket;

    connect(udpSocket, SIGNAL(readyRead()) ,
    this, SLOT(readyRead()),
    Qt::DirectConnection);

    // bind the socket
    this->bind();

    // execute
    exec();
    }

    RemoteConnection::bind()
    {
    // host port may have changed, so close current port and open new port
    udpSocket->close();
    udpSocket->bind( this->getHostPort() );
    }

    void RemoteConnection::setHostPort(unsigned int hostPort)
    {
    this->hostPort = hostPort;
    bind();
    }
    @

    In the main loop I create my RemoteConnection object and tell it to start a new thread. In RemoteConnection's run() method I create a QUdpSocket object and connect it's readyRead() signal to RemoteConnection's readyRead Slot. After that I call the bind() method of RemoteConnection to bind the socket to the specified port.

    During the execution of the programm it might happen that the user changes the port the socket listens on. In that case, RemoteConnection::setHostPort() is called from the main-thread and I have to rebind the socket object. But that causes the following error during execution of the programm:

    ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 2d8ea00. Receiver...''

    I understand that my remoteConnection object and the udpSocket object run in different threads, because I created the socket object in RemoteConnection's run() method.

    But is there a way to manipulate the socket object from a different thread?
    Or do I have to create a wrapper class for QUdpSocket and assign a signal like "hostPortChanged()"?

    I know maybe it's not the best solution to create the udpSocket object in a different thread. But unfortunately I have no proper access to the main app and can't change the whole architecture of the programm.

    Any ideas?

    Thanks a lot for your help!

    1 Reply Last reply
    0
    • G Offline
      G Offline
      goetz
      wrote on last edited by
      #2

      Did you read "Threads, Events and QObjects":http://developer.qt.nokia.com/wiki/Threads_Events_QObjects from the wiki already?

      http://www.catb.org/~esr/faqs/smart-questions.html

      1 Reply Last reply
      0
      • M Offline
        M Offline
        Mr.Orange
        wrote on last edited by
        #3

        Hello Volker,
        thanks for the advice! Yes, I've already read it. Unfortunately neither of the provided solutions there seems to work for me.

        I tried

        @QMetaObject::invokeMethod(udpSocket, "bind", Q_ARG(unsigned int, this->hostPort))@

        at the RemoteConnection's bind() method but it returned false, because the QUdpSocket's bind() method is not a slot. I understand that there is a way to move objects from one thread to another, but I didn't quite get the concept behind it an where (in the code) to move the object.

        Anyone done something similar?

        Thanks a lot for your help!
        Greets,

        Mr.Orange

        1 Reply Last reply
        0
        • A Offline
          A Offline
          andre
          wrote on last edited by
          #4

          Lets start at the beginning here. Why do you think you need a new thread for your socket in the first place?

          1 Reply Last reply
          0
          • M Offline
            M Offline
            Mr.Orange
            wrote on last edited by
            #5

            Hey Andre,

            thanks for the reply. I don't know if I actually need a new thread for the socket. The piece of software I'm currently working on was created by someone else and has grown over the years to a very complex system. The problem with the port binding is a bug nobody discovered before, because it used to be very unlikely to change the host port during runtime. But now I need this functionality, so I gotta fix this bug :).

            Thanks for your help!
            Greets,
            Mr.Orange

            1 Reply Last reply
            0
            • A Offline
              A Offline
              andre
              wrote on last edited by
              #6

              Well, the core of your problem is actually described in the article Volker linked to. Here is what you are doing:

              You are creating an object of class RemoteConnection in thread 1. When you start the thread that RemoteConnection manages (yes, QThread manages a thread, which is not the same as being a thread!), the code in RemoteConnection::run() is executed in the context of the newly created thread 2. So, your udpSocket is created in thread 2. Note that your RemoteConnection instance itself is still (and should remain in!) thread 1.

              Now, you try to change the port binding. You do that by calling RemoteConnection::setHostPort(), which in turn calls RemoteConnection::bind(). These calls will probably be executed in the context of thread 1. However, you are manipulating an object that lives in thread 2. Without savety precautions, that is unsave!

              Note that also the direct connection between the UDP socket's signal and the RemoteConnection readyRead slot is unsafe.

              You have several potential ways to solve this:

              get rid of the thread. It is not needed, unless you also do extensive processing on incomming or outgoing data within the thread itself, or

              Make RemoteConnection inherit QObject (not inherited from QThread), and instead do something like this:

              @
              RemoteConnection* connection = new RemoteConnection(0);
              QThread* socketThread = new QThread(this);
              connection->moveToThread(socketThread);
              socketThread->start();
              @

              Then, make the methods in your RemoteConnection object slots, and only call them using queued (or automatic) connections.

              You could even do something like this for your methods in RemoteConnection:
              @
              class RemoteConnection:public QObject
              {
              //...
              public slots:
              void setHostPort(unsigned int hostPort);

              private slots:
              void setHostPortImplementation(unsigned int hostPort);
              }

              //in implementation:
              void RemoteConnection::setHostPort(unsigned int hostPort)
              {
              if (QThread::currentThread() == thread()) {
              //ok, this method is called from the same thread we live in so do direct method call
              setHostPortImplementation(hostPort);
              } else {
              //method is called from outside our own thread, do save invocation instead
              QMetaObject::invokeMethod(this, "setHostPortImplementation", Q_ARG(unsigned int, hostPort), Qt::QueuedConnection);
              }
              }
              @

              If you protect your methods this way, you can be sure that they are always savely called.

              The preferred method, IMHO, is to get rid of unneeded threads so you can avoid all this hassle.

              1 Reply Last reply
              0
              • M Offline
                M Offline
                Mr.Orange
                wrote on last edited by
                #7

                Andre,

                thank you so much for your detailed reply! I think I'll try to get rid of the threads somehow. Then do some excessive testing and hope that everything works fine.
                I probably won't finish today, but after the weekend I'll give you a short feedback on my progress.

                Thanks again for your help & have a nice weekend!
                Greets,
                Mr.Orange

                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