qtqt5qthreadunix-socketdatagram

The signal emmited from UnixDomain datagram socket server running on a QThread never received


The Files which I've created

I've created WorkerThread class extended QThread as follow:

// https://stackoverflow.com/a/14546167/11073131
#ifndef THREAD_H
#define THREAD_H

#include <QThread>
#include <QDebug>
#include <QString>
#include "ud_ucase.h"

class WorkerThread : public QThread {
  Q_OBJECT
  public:
    void run() {
      struct sockaddr_un svaddr, claddr;
      int sfd, j;
      ssize_t numBytes;
      socklen_t len;
      char buf[BUF_SIZE];

      sfd = socket(AF_UNIX, SOCK_DGRAM, 0);       // Create server socket
      if (sfd == -1)
          qDebug() << "socket";

      // Construct well-known address and bind server socket to it

      if (strlen(SV_SOCK_PATH) > sizeof(svaddr.sun_path) - 1)
          printf("Server socket path too long: %s", SV_SOCK_PATH);

      if (remove(SV_SOCK_PATH) == -1 && errno != ENOENT)
          printf("remove-%s", SV_SOCK_PATH);

      memset(&svaddr, 0, sizeof(struct sockaddr_un));
      svaddr.sun_family = AF_UNIX;
      strncpy(svaddr.sun_path, SV_SOCK_PATH, sizeof(svaddr.sun_path) - 1);

      if (bind(sfd, (struct sockaddr *) &svaddr, sizeof(struct sockaddr_un)) == -1)
          printf("bind");

      // Receive messages, convert to uppercase, and return to client
      while(1) {
          len = sizeof(struct sockaddr_un);
          qDebug() << "before recvfrom";
          emit progressChanged("before recvfrom");     // 1st emit
          numBytes = recvfrom(sfd, buf, BUF_SIZE, 0,
                              (struct sockaddr *) &claddr, &len);

          if (numBytes == -1)
              printf("recvfrom");

          printf("Server received %ld bytes from %s\n", (long) numBytes,
                  claddr.sun_path);

          buf[numBytes] = 0;
          qDebug() << "buf:" << buf;
          emit progressChanged(buf);            // 2nd emit
          qDebug() << "Emit finished";

          // Convert received messages to uppercase
          for (j = 0; j < numBytes; j++)
              buf[j] = toupper((unsigned char) buf[j]);

          // Return back it to client socket
          if (sendto(sfd, buf, numBytes, 0, (struct sockaddr *) &claddr, len) !=
                  numBytes)
              printf("sendto");
      }
/*
      while(1) {
            // ... hard work
            // Now want to notify main thread:
            emit progressChanged("Some info");
      }
*/
    }
  // Define signal:
  signals:
    void progressChanged(QString info);
};
#endif // THREAD_H

The run() method of the above class creates & runs a UnixDomain datagram socket server that emits the received string by a signal progressChanged(QString info), converts this string to uppercase, then returns to the client socket.

The slot that receives the signal mentioned above is defined on the mainwindow.cpp as follow:

void MainWindow::onProgressChanged(QString info) {
    // Processing code
    ui->label->setText("Latest info: " + info);
    qDebug() << "Signal received:" << info;
}

void MainWindow::startWorkInAThread() {
    // Create an instance of your woker
    WorkerThread *workerThread = new WorkerThread;
    // Connect our signal and slot
    connect(workerThread, SIGNAL(progressChanged(QString)),
                          SLOT(onProgressChanged(QString)));
    // Setup callback for cleanup when it finishes
    connect(workerThread, SIGNAL(finished()),
            workerThread, SLOT(deleteLater()));
    // Run, Forest, run!
    workerThread->start(); // This invokes WorkerThread::run in a new thread
}

The function MainWindow::startWorkInAThread() connecting the signal and the slot is called in the constructor of MainWindow.

Running result

When runs this application, the 1st emit is received by MainWindow::onProgressChanged(QString info) with following QDebug message:

before recvfrom
Signal received: "before recvfrom"

Then, send message "aho" from external socket client, the QDebug message indicates that the 2nd emit, which emit the signal with received message by recvfrom() function of the socket, was emitted, but never received. Also next 1st emit also was emitted but never received.

before recvfrom
Signal received: "before recvfrom"
buf: aho
Emit finished
before recvfrom

And the socket client receive uppercase converted message as expected.

./ud_ucase_cl aho
Response 1: AHO

Question

Considering that

Why after recvfrom() on the Qthread class any message get never received? How Can I fix this?

Environment

Qt 5.11.3 Raspberry Pi 2B+ Linux raspberrypi 5.10.103-v7+ #1529 SMP Tue Mar 8 12:21:37 GMT 2022 armv7l GNU/Linux GCC 8.3.0


Solution

  • I've succeeded in making a minimum reproducing environment as datagramRep, and I've found the most suspicious stuff is the QTimer object in mainwindow.cpp because the issue doesn't reproduce when I don't call timer->start(1000) or increases interval to 60000. The above threshold might be device-dependent. I've confirmed this with my RPi 2 Model B.

    It seems weird why a 1-second interval QTimer blocks to receive Signal. But it might be so.