0

I am writing the client for a client/server application. The clients are supposed to login using a login window. If the login is successful, a "waiting" window appears (this is just a window that contains a label). On the server side there is a barrier that waits for n clients to be logged in; when this happens, a message is broadcasted, the waiting window is supposed to close and a new window appears for every client.

The networking interface is implemented by me, using low-level functions, not the functionality provided by Qt.

The actual waiting loop is something like this:

char buffer[256];
while (strcmp(buffer, "proceed"))
    read(sockfd, buffer, 256);

The problem is that if I start this loop in the main thread, the application blocks, for obvious reasons.

How can I make this loop run and not block the application, and close the dialog when it ends?

Later edit: I did also attempt to use QThreads, but, for reasons which I don't fully understand yet, the application still crashes:

class WaitLoop : public QThread {
public:
    WaitLoop(NetworkHandler &network) : network(network) {}
private :
    NetworkHandler &network;
    void run() {
        this->network.waitForGameStart();
    }
};

In the wait dialog constructor:

WaitLoop *waitLoop = new WaitLoop(network);
connect(waitLoop, SIGNAL(finished()), this, SLOT(gameStartSlot()));
waitLoop->start();

The application still crashes using this approach.

Paul92
  • 8,827
  • 1
  • 23
  • 37
  • maybe a progress dialog could work; http://doc.qt.io/qt-4.8/qprogressdialog.html – john elemans Feb 05 '16 at 15:46
  • implement a worker object with this functionality and run in a separate thread. when you receive the message, just emit a signal from the worker object which you can connect to `close()` slot of the dialog. – ramtheconqueror Feb 05 '16 at 16:33
  • @ramtheconqueror Now you have two problems, instead of one. Threads aren't a universal remedy. – Kuba hasn't forgotten Monica Feb 05 '16 at 16:35
  • It crashes because your `NetworkHandler` isn't thread-safe. One can't answer this question without seeing all the relevant code that reproduces the issue. It should be closed unless you provide a *complete* example of crashing code. Minimize it. It likely doesn't have to be longer than 30-40 lines, otherwise you're doing something wrong. – Kuba hasn't forgotten Monica Feb 05 '16 at 18:30

1 Answers1

0

The sanest way to approach this would not be using low-level functions, because you aren't writing in C. Use at least QAbstractSocket to wrap a sockfd. The setSocketDescriptor method lets you do it.

Your code then becomes non-blocking and asynchronous:

class Controller : public QObject {
  Q_OBJECT
  QStateMachine m_sm;
  QState s_init{&m_sm}, s_proceeding{&m_sm};
  QAbstractSocket m_socket;
  Q_SIGNAL void proceed();
  Q_SLOT void onData() {
    auto data = m_socket.readAll();
    if (data.contains("proceed")) proceed();
  }
public:
  Controller(QObject * parent = 0) : QObject(parent) {
    connect(&m_socket, &QIODevice::readyRead, this, &Controller::onData);
    s_init.addTransition(this, &Controller::proceed, &s_proceeding);
    m_sm.setInitialState(&s_init);
    m_sm.start();
  }
  bool setup(quintptr fd) {
    return m_socket.setSocketDescriptor(fd);
  }
};

Through the use of a state machine, it's easy to add more states, react to their transitions (see QState::onEntry signal, etc.), and ensure that the behavior is correct. Fleshing out a UML statechart forces you to think about handling corner cases, etc. See this answer for a full example.

Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • I know that this is the best practice and combining low level network handling routines with high level code is not the best approach. However, this is a requirement of the application, to use low level network handling routines. – Paul92 Feb 05 '16 at 16:47
  • @Paul92 Integrate the file descriptors used in your networking with the event loop, then. Your library, when properly designed, should give a set of file descriptors to the user, and let the user notify *it* when any activity is detected on the fds. Your Qt code will then use a `QSocketNotifier` to call back into your library, someone else's code might use glib's event loop, or just a plain `poll` or, shudder, `select`. Otherwise, put it all into a thread. I don't know what you're doing wrong, you don't show nearly enough code. – Kuba hasn't forgotten Monica Feb 05 '16 at 18:27