Multithread HTTP server



  • Ey all guys,
    I've tried this piece of code:
    http://doc.qt.digia.com/solutions/4/qtservice/qtservice-example-server.html

    On my server implementation, each http request read and write data from usb. With this code, two simultaneous request interferes with usb communication. I've try to use QMutex, but doesn't works: still locked on the first pass.

    Any ideas?
    Thanks!
    :-)



  • I would suggest to use a queue for the requests.
    A single thread reads all requests from the queue, reads, writes data from an usb.

    To help you with the current implementation we need to see the code.
    Coould you post it here.



  • #ifndef HTTPSERVER_H
    #define HTTPSERVER_H
    #include <qtcpserver.h>
    #include <QTcpSocket.h>
    #include <qdatetime.h>
    #include <QHostAddress.h>
    #include <QUrlQuery>
    #include <QWidget>
    #include <QRunnable>
    #include <QThreadPool>
    
    class HttpServer : public QTcpServer
    {
        Q_OBJECT
    public:
        HttpServer(quint16 port, QObject* parent, QWidget* w)
            : QTcpServer(parent), disabled(false)
        {
            this->widget = w;
            this->d.Init();
            qDebug()<< "Device Init() OK";
            if (!listen(QHostAddress::Any, port)) {
                qDebug()<<"FATAL Failed to bind " + this->serverAddress().toString() + ":" + QString::number(this->serverPort());
            } else {
                qDebug()<<"Server OK "+ this->serverAddress().toString() + ":" + QString::number(this->serverPort());
            }
        }
    
        void incomingConnection(qintptr socket)
        {
            if (disabled)
                return;
    
            //HttpServerTask *hello = new HttpServerTask(socket);
            //QThreadPool::globalInstance()->start(hello);
    
            // When a new client connects, the server constructs a QTcpSocket and all
            // communication with the client is done over this QTcpSocket. QTcpSocket
            // works asynchronously, this means that all the communication is done
            // in the two slots readClient() and discardClient().
            QTcpSocket* s = new QTcpSocket(this);
            connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
            connect(s, SIGNAL(disconnected()), this, SLOT(discardClient()));
            s->setSocketDescriptor(socket);
    
            qDebug() <<"Socket " + QString::number(socket) + " new connection";
        }
    
        void pause()
        {
            disabled = true;
        }
    
        void resume()
        {
            disabled = false;
        }
    
    private slots:
        void readClient()
        {
            if (disabled)
                return;
    
            // This slot is called when the client sent data to the server. The
            // server looks if it was a get request and sends a very simple HTML
            // document back.
            try {
                QTcpSocket* socket = (QTcpSocket*)sender();
                qDebug() <<"Socket " + QString::number(socket->socketDescriptor()) + " read";
                QByteArray buffer = socket->readAll();
                this->readData = this->readData.append(buffer);
                if ( this->readData.endsWith( "\r\n" ) ) {
                    QStringList tokens = QString( this->readData ).split(" ");
                    QStringList fulluri = tokens[1].split("?");
                    QString query = "";
                    QString uri = tokens[1];
                    if (fulluri.count() > 1) {
                        uri = fulluri[0];
                        query = fulluri[1];
                    }
    
                    qDebug() <<this->serverAddress().toString() + ":" + QString::number(this->serverPort()) + " " + tokens[0] + " " + tokens[1];
                    if (tokens[0] == "GET") {
                        GetMethod(socket, uri, query);
                        closeSocket(socket);
                        this->readData.clear();
                    } else if(tokens[0] == "POST") {
                        int index = this->readData.indexOf( "\r\n\r\n" );
                        if ( ( 0 < index ) && ( index + 4 < this->readData.length() ) ) {
                            QString data = QString( this->readData.mid( index + 4, this->readData.length() - index - 6 ) );
                            qDebug() <<data;
                            PostMethod(socket, uri, query, data);
                            closeSocket(socket);
                            this->readData.clear();
                        }
    
                    }
                }
            } catch(std::exception ex) {
                qCritical() << "FATAL HTTP readClient() EXCEPTION, WHAT="<< qPrintable(ex.what());
                this->readData.clear();
            } catch (...) {
                qCritical() << "FATAL HTTP readClient() EXCEPTION";
                this->readData.clear();
            }
        }
        void discardClient()
        {
            QTcpSocket* socket = (QTcpSocket*)sender();
            socket->deleteLater();
    
            qDebug() <<"Connection closed";
            if (requireExit) {
                exit(0);
            }
        }
    protected:
        void PostMethod(QTcpSocket* socket, QString uri, QString query, QString data);
        void GetMethod(QTcpSocket* socket, QString uri, QString query);
    
    
    private:
        QByteArray readData;
        bool disabled;
        bool requireExit = false;
        //QMutex mutex;
        QWidget* widget;
    
        QString currentSelectedFolder;
    
    
        void writeOut(QTcpSocket* socket, QString content_type, QString result) {
            QTextStream os(socket);
            os.setAutoDetectUnicode(true);
            os << "HTTP/1.0 200 Ok\r\n"
                  "Content-Type: "+ content_type +"; charset=\"utf-8\"\r\n"
                                                  "\r\n" + result;
            socket->flush();
        }
        void closeSocket(QTcpSocket* socket) {
            socket->close();
            //qDebug() <<"Socket " + QString::number(socket->socketDescriptor()) + " connection closed";
            if (socket->state() == QTcpSocket::UnconnectedState) {
                delete socket;
            }
        }
    };
    
    
    #endif // HTTPSERVER_H
    

    [edit: Added missing coding tags SGaist]



  • This is the .cpp file:

    void HttpServer::GetMethod(QTcpSocket* socket, QString uri, QString query) {
        QString content_type = "text/plain";
        QString result = "";
        if (uri == "/command1") {
            //QMutexLocker locker(&this->mutex);
            // READ & WRITE USB SYNCRONOUSLY
            // this MUTEX doesn't works!!!! :(
            //locker.unlock();
        } else if (uri == "/command2") {
             // READ & WRITE USB SYNCRONOUSLY
        } else if (uri == "/ping") {
            result = "pong";
    
        } else if (uri == "/quit") {
            //QMutexLocker locker(&this->mutex);
            this->close();
            d.Close();
            //locker.unlock();
            requireExit = true;
            result = "done";
        } else if ( uri.endsWith( ".html" )   ||
                    uri.endsWith( ".htm"  )   ||
                    uri.endsWith( ".txt"  )   ||
                    uri.endsWith( ".xml"  ) ) {
            QString filename = QApplication::applicationDirPath() + "/doc_root" + uri;
            if ( QFile::exists( filename ) ) {
                QFile file( filename );
                if ( file.open( QFile::ReadOnly | QFile::Text ) ) {
                    QTextStream in( &file );
                    result = in.readAll();
                    file.close();
                }
            }
        }
    
        writeOut(socket, content_type, result);
        qDebug() <<"Response: "<< result;
    }
    
    
    void HttpServer::PostMethod(QTcpSocket* socket, QString uri, QString query, QString data) {
        QString content_type = "text/plain";
        QString result = "";
        if (uri == "/largedatacommand") {
            //QMutexLocker locker(&this->mutex);
            // READ & WRITE USB SYNCRONOUSLY
            // this MUTEX doesn't works!!!! :(
            //locker.unlock();
        } 
        writeOut(socket, content_type, result);
        qDebug() <<"Response: "<< result;
    }
    

    That's all.
    PS: how to formatting code in this new forum?? ;) thanks!

    [edit: added missing coding tags SGaist]


  • Lifetime Qt Champion

    Hi,

    The code formatting is: three ` (back sticks), start the code on a new line and again three to close the block



  • Thanks SGaist!

    I've found this example: http://doc.qt.io/qt-5/qtnetwork-threadedfortuneserver-example.html

    May be the right way!



  • Maybe my HTTP server library helps you: http://stefanfrings.de/qtwebapp/index-en.html
    It's multi-threaded.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.