Solved Multithreading with QTcpServer
-
I am trying to develop a HTTP server with QTcpServer and QThread, here is my code:
Server class:
Server::Server(QObject *parent) : QObject(parent) , m_server(new QTcpServer(this)) , m_serverMutex(new QMutex) , m_thread1(new WorkerThread(m_server, m_serverMutex, this)) , m_thread2(new WorkerThread(m_server, m_serverMutex, this)) { connect(this, SIGNAL(startThread()), m_thread1, SLOT(start())); connect(this, SIGNAL(startThread()), m_thread2, SLOT(start())); connect(m_server, SIGNAL(newConnection()), m_thread1, SLOT(onNewConnection()), Qt::QueuedConnection); connect(m_server, SIGNAL(newConnection()), m_thread2, SLOT(onNewConnection()), Qt::QueuedConnection); } void Server::run() { emit startThread(); qDebug() << "Start to listen 3000 port"; m_server->listen(QHostAddress::LocalHost, 3000); }
Thread class:
#include "workerthread.h" #include <QTcpSocket> WorkerThread::WorkerThread(QObject *parent = 0) : QThread(parent) { } WorkerThread::WorkerThread(QTcpServer* server, QMutex *serverMutex, QObject *parent) : QThread(parent) , m_server(server) , m_serverMutex(serverMutex) { } void WorkerThread::run() { qDebug() << "Start thread: " << QThread::currentThreadId(); exec(); } void WorkerThread::onNewConnection() { qDebug() << "Getting signal from thread: " << QThread::currentThreadId(); m_serverMutex->lock(); if (m_server->hasPendingConnections()) { qDebug() << "Accept a new connection from thread: " << QThread::currentThreadId(); QTcpSocket *socket = m_server->nextPendingConnection(); m_serverMutex->unlock(); connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead())); } } void WorkerThread::onReadyRead() { QObject* object = sender(); QTcpSocket *socket = qobject_cast<QTcpSocket*>(object); QByteArray data = socket->readAll(); socket->write("HTTP/1.1 200 OK\n\n"); socket->close(); }
This code works when only one thread connect to the
QTcpServer
, however, when I use two threads to receive the signal it doesn't work.At first, my opinion is that make all WorkerThread to compete
m_serverMutex
, if it compete the mutex successfully, it can get the socket withQTcpServer::nextPendingConnection()
.Why this code doesn't work when using multi-thread?
-
@FloatFlower.Huang said in Multithreading with QTcpServer:
void WorkerThread::onNewConnection()
{
qDebug() << "Getting signal from thread: " << QThread::currentThreadId();
m_serverMutex->lock();
if (m_server->hasPendingConnections()) {
qDebug() << "Accept a new connection from thread: " << QThread::currentThreadId();
QTcpSocket *socket = m_server->nextPendingConnection();
m_serverMutex->unlock();
connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
}
}You don't unlock the mutex! Use QMutexLocker - it will unlock mutex automatically when going out of scope.
-
@jsulm
In my code, even theqDebug() << "Getting signal from thread: " << QThread::currentThreadId();
doesn't print anything, which means itonNewConnection()
doesn't receive the signal. Why signal cannot emit to multiple thread? -
@FloatFlower.Huang Did you start the threads?
Also why do you want to send same signal to multiple threads? You should send it to the next free thread. -
@jsulm
Yes, I started them.
Because QTcpSocket is a non-blocking socket, so one thread can handle lots of sockets. As the result, I want to create 4 threads to compete the incomming socket when the new connection is available. -
@FloatFlower.Huang said in Multithreading with QTcpServer:
so one thread can handle lots of sockets
I would actually handle only one connection per thread at a given time.
And I would not connect each thread to newConnection() signal. Instead I would connect newConnection() signal to a slot in a manager class which then spawns a new thread (or, even better, reuses a free thread from a thread pool) and delegates the connection to that thread. -
If I use socket-pre-thread approach there will a lot of thread when concurrent connections come.
If I put socket inQRunnable
, putting them toQThreadPool
and set the max threads to 4. As every socket, I am going to waiting for all data arriving, and handle the request, and then write response to socket.
And then if I want to send a large file to client, in HTTP, the file should be split into smaller package to send, also, in TCP, we should wait for the signalbytesWritten()
emitted and then we can send the next package. If we want to wait the signal, we should create aQEventLoop
inQRunnable
, each socket will occupy a thread. If there are 4 sockets request a large file and I need to send the file package by package, and the 4 threads will be in used, as the result, the next connection cannot be handle in time.But your solution is the best, thank you so much.