C++ Tutorial: Sockets - Server & Client using QT - 2015

http://www.bogotobogo.com/cplusplus/sockets_server_client_QT.php

Updates: This tutorial was written with Qt4.8. But some of them (e.g. QHttp) have been deprecated in Qt5. We can get updates from Qt5 tutorials:

  • Qt QHttp - Downloading Files
  • Qt 5 QNetworkAccessManager and QNetworkRequest - Downloading Files
  • Qt 5 QTcpSocket
  • Qt 5 QTcpSocket with Signals and Slots
  • Qt 5 QTcpServer - Client and Server
  • Qt 5 QTcpServer - Client and Server using MultiThreading
  • QTcpServer - Client and Server using QThreadPool
  • Asynchronous QTcpServer - Client and Server using QThreadPool


HTTP Download


The code below do download a header file from http://www.bogotobogo.com/

Files including project file are DoHttp.tar.

First, let Qt project (DoHttp.pro) know we're going to use module, network.


QT       += core
QT       += network

QT       -= gui

TARGET = DoHttp
CONFIG   += console
CONFIG   -= app_bundle

TEMPLATE = app


SOURCES += main.cpp \
    downloader.cpp

HEADERS += \
    downloader.h


main.cpp should look like this:

#include <QCoreApplication>
#include "downloader.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    downloader cDown;
    cDown.Do_Download();

    return a.exec();
}

The downloader class files: downloader.h

// downloader.h

#ifndef DOWNLOADER_H
#define DOWNLOADER_H

#include <QObject>
#include <QHttp>
#include <QFile>
#include <QDebug>


class downloader : public QObject
{
    Q_OBJECT
public:
    explicit downloader(QObject *parent = 0);

    void Do_Download();

signals:

public slots:
    void stateChanged(int state);
    void responseHeaderReceived ( const QHttpResponseHeader & resp );
    void requestFinished ( int id, bool error );

private:
    QHttp *http;

};

#endif // DOWNLOADER_H                       



downloader.cpp:

#include "downloader.h"

downloader::downloader(QObject *parent) :
    QObject(parent)
{
}

void downloader::Do_Download()
{
    http = new QHttp(this);

    connect(http, SIGNAL(stateChanged(int)), this, SLOT(stateChanged(int)));
    connect(http, SIGNAL(responseHeaderReceived(QHttpResponseHeader)),
            this, SLOT(responseHeaderReceived(QHttpResponseHeader)));
    connect(http, SIGNAL(requestFinished(int,bool)),
                    this, SLOT(requestFinished(int,bool)));

    http->setHost("bogotobogo.com");

    // get default file
    http->get("/");

}

void downloader::stateChanged(int state)
{
    switch(state)
    {
        case 0:
            qDebug() << "Unconnected";
            break;
        case 1:
            qDebug() << "Host Lookup";
            break;
        case 2:
            qDebug() << "Connecting";
            break;
        case 3:
            qDebug() << "Sending";
            break;
        case 4:
            qDebug() << "Reading";
            break;
        case 5:
            qDebug() << "Connect";
            break;
        case 6:
            qDebug() << "Closing";
            break;
    }
}

void downloader::responseHeaderReceived ( const QHttpResponseHeader & resp )
{
    qDebug() << "Size" << resp.contentLength();
    qDebug() << "Type" << resp.contentType();
    qDebug() << "Status" << resp.statusCode();
}

void downloader::requestFinished ( int id, bool error )
{
    if(error)
    {
        qDebug() << "ERROR!";
    }
    else
    {
        qDebug() << "OK!";
        QFile *file = new QFile("/home/KHong/DoHttp/downloaded.txt");
        if(file->open(QFile::Append))
        {
            file->write(http->readAll());
            file->flush();
            file->close();
        }
        delete file;
    }
}

Here is a table that shows the enum for all states:

enum QHttp::State

Constant Value Description
QHttp::Unconnected 0 There is no connection to the host.
QHttp::HostLookup 1 A host name lookup is in progress.
QHttp::Connecting 2 An attempt to connect to the host is in progress.
QHttp::Sending 3 The client is sending its request to the server.
QHttp::Reading 4 The client's request has been sent and the client is reading the server's response.
QHttp::Connected 5 The connection to the host is open, but the client is neither sending a request, nor waiting for a response.
QHttp::Closing 6 The connection is closing down, but is not yet closed. (The state will be Unconnected when the connection is closed.)


The output from the run:

OK! 
Connecting 
Sending 
Reading 
Size 285 
Type "text/html" 
Status 200 
Connect 
OK! 
Closing 
Unconnected 
QTcpSocket - Basics


The following codes makes a connection to a host using Q3Socket::connectToHost().

Here is the description of connectToHost():

Attempts to make a connection to host on the specified port and return immediately.

Any connection or pending connection is closed immediately, and Q3Socket goes into the HostLookup state. When the lookup succeeds, it emits hostFound(), starts a TCP connection and goes into the Connecting state. Finally, when the connection succeeds, it emits connected() and goes into the Connected state. If there is an error at any point, it emits error().

host may be an IP address in string form, or it may be a DNS name. Q3Socket will do a normal DNS lookup if required. Note that port is in native byte order, unlike some other libraries.


Here is the file used in this section: SocketTest.tar.gz

// main.cpp

#include <QCoreApplication>
#include "sockettest.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    SocketTest cTest;
    cTest.Connect();

    return a.exec();
}


sockettest.h

#ifndef SOCKETTEST_H
#define SOCKETTEST_H

#include <QObject>
#include <QTcpSocket>
#include <QDebug7gt;

class SocketTest : public QObject
{
    Q_OBJECT
public:
    explicit SocketTest(QObject *parent = 0);

    void Connect();

signals:

public slots:

private:
    QTcpSocket *socket;

};

#endif // SOCKETTEST_H


sockettest.cpp

#include "sockettest.h"

SocketTest::SocketTest(QObject *parent) :
    QObject(parent)
{
}

void SocketTest::Connect()
{
    socket = new QTcpSocket(this);
    socket->connectToHost("bogotobogo.com", 80);

    if(socket->waitForConnected(3000))
    {
        qDebug() << "Connected!";

        // send
        socket->write("hello server\r\n\r\n\r\n\r\n");
        socket->waitForBytesWritten(1000);
        socket->waitForReadyRead(3000);
        qDebug() << "Reading: " << socket->bytesAvailable();

        qDebug() << socket->readAll();

        socket->close();
    }
    else
    {
        qDebug() << "Not connected!";
    }

    // sent

    // got

    // closed
}


Output should look like this:

Connected! 
Reading:  414 
"



Bad Request

Your browser sent a request that this server could not understand.

Additionally, a 404 Not Found error was encountered while trying to use an ErrorDocument to handle the request.

Apache Server at just78.justhost.com Port 80 "

Unfortunately, I got bad request though I got some response from the server. I'll fix this problem later. Any comments on the code will be very much appreciated ([email protected]).




QTcpSocket - Signal/Slot


Here is the file used in this section: SignalSocket.tar.gz

In this section, as a continuous series of QTcpSocket, we used the signal/slot mechanism of Qt.

Here is our main() function:

// main.cpp

#include <QCoreApplication>
#include "sockettest.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    SocketTest mTest;
    mTest.Test();
    
    return a.exec();
}


Header file:

// sockettest.h

#ifndef SOCKETTEST_H
#define SOCKETTEST_H

#include <QObject>
#include <QDebug>
#include <QTcpSocket>
#include <QAbstractSocket>


class SocketTest : public QObject
{
    Q_OBJECT
public:
    explicit SocketTest(QObject *parent = 0);
    void Test();
    
signals:
    
public slots:

    void connected();
    void disconnected();
    void bytesWritten(qint64 bytes);
    void readyRead();

private:
    QTcpSocket *socket;
    
};
#endif // SOCKETTEST_H


Then, the implementation file:

// SocketTest.cpp

#include "sockettest.h"

SocketTest::SocketTest(QObject *parent) :
    QObject(parent)
{
}

void SocketTest::Test()
{
    socket = new QTcpSocket(this);
    connect(socket, SIGNAL(connected()), this, SLOT(connected()));
    connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
    connect(socket, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64)));

    qDebug() << "Connecting,..";

    socket->connectToHost("bogotobogo.com", 80);

    if(!socket->waitForDisconnected(1000))
    {
        qDebug() << "Error: " << socket->errorString();
    }

}

void SocketTest::connected()
{
    qDebug() << "Connected!";

    socket->write("HEAD / HTTP/1.0\r\n\r\n\r\n\r\n");
}

void SocketTest::disconnected()
{
    qDebug() << "Disconnected!";
}

void SocketTest::bytesWritten(qint64 bytes)
{
    qDebug() << "We wrote: " << bytes;
}

void SocketTest::readyRead()
{
    qDebug() << "Reading...";
    qDebug() << socket->readAll();
}

Output looks like this:

Connected! 
Connected! 
Connected! 
Reading... 
"HTTP/1.1 200 OK
Date: Mon, 08 Apr 2013 23:50:29 GMT
Server: Apache
Accept-Ranges: bytes
Vary: Accept-Encoding
Connection: close
Content-Type: text/html

" 
Disconnected! 




QTcpServer - Basic Application


In this section, we're doing very basic server/client talks with Qt.

While the server is listening, the client tries to connect to the server. Here are two screen shots from server and client:


C++ Tutorial: Sockets - Server & Client using QT - 2015_第1张图片

As we can see from the picture above, the server has been started and listening.
The client did telnet to loopback (127.0.0.1) with the listening port 1234, and got the response from the server hello client.

Here is the file used in this section: MyFirstServer.tar.gz

main.cpp:

// main.cpp

#include <QCoreApplication>
#include "myserver.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MyServer mServer;
    
    return a.exec();
}


Header file, myserver.h:

#ifndef MYSERVER_H
#define MYSERVER_H

#include <QObject>
#include <QDebug>
#include <QTcpServer>
#include <QTcpSocket>

class MyServer : public QObject
{
    Q_OBJECT
public:
    explicit MyServer(QObject *parent = 0);
    
signals:
    
public slots:
    void newConnection();

private:
    QTcpServer *server;
    
};

#endif // MYSERVER_H


Implementation file, myserver.cpp:

// myserver.cpp

#include "myserver.h"

MyServer::MyServer(QObject *parent) :
    QObject(parent)
{
    server = new QTcpServer(this);

    connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));

    if(!server->listen(QHostAddress::Any, 1234))
    {
        qDebug() << "Server could not start!";
    }
    else
    {
        qDebug() << "Server started!";
    }
}

void MyServer::newConnection()
{
    QTcpSocket *socket = server->nextPendingConnection();

    socket->write("hello client\r\n");
    socket->flush();

    socket->waitForBytesWritten(3000);

    socket->close();
}




QTcpServer - Multithread Client/Server


The resulting screen shots of the code are in the picture below.



The picture at the top is a server screen shot. It was listening and then got connected from the two clients whose IDs are 7 and 9, respectively. Server printed out the message it got from those clients. The two pictures below the server screen shot are from two clients, ID(7) and ID(9). They echoed their messages to servers.

Note that whenever there is a new connection, a new thread will be created.

Here is the file used in this section: MultiServer.tar.gz


main.cpp

#include <QCoreApplication>
#include "myserver.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    
    MyServer Server;
    Server.StartServer();

    return a.exec();
}


hearder file, myserver.h

// myserver.h

#ifndef MYSERVER_H
#define MYSERVER_H

#include <QTcpServer>
#include <QDebug>
#include "mythread.h"

class MyServer : public QTcpServer
{
    Q_OBJECT
public:
    explicit MyServer(QObject *parent = 0);
    void StartServer();
    
signals:
    
public slots:

protected:
    void incomingConnection(int socketDescriptor);
    
};

#endif // MYSERVER_H


Implementation file, myserver.cpp

// myserver.cpp

#include "myserver.h"

MyServer::MyServer(QObject *parent) :
    QTcpServer(parent)
{
}

void MyServer::StartServer()
{
    if(!this->listen(QHostAddress::Any,1234))
    {
        qDebug() << "Could not start server";
    }
    else
    {
        qDebug() << "Listening...";
    }
}

void MyServer::incomingConnection(int socketDescriptor)
{
    qDebug() << socketDescriptor << " Connecting...";
    MyThread *thread = new MyThread(socketDescriptor, this);
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    thread->start();
}


Another header, mythread.h

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QTcpSocket>
#include <QDebug>

class MyThread : public QThread
{
    Q_OBJECT
public:
    explicit MyThread(int iID, QObject *parent = 0);
    void run();
    
signals:
    void error(QTcpSocket::SocketError socketerror);
    
public slots:
    void readyRead();
    void disconnected();

public slots:

private:
    QTcpSocket *socket;
    int socketDescriptor;
    
};

#endif // MYTHREAD_H


Finally, another implementation file, mythread.cpp

// mythread.cpp

#include "mythread.h"

MyThread::MyThread(int ID, QObject *parent) :
    QThread(parent)
{
    this->socketDescriptor = ID;
}

void MyThread::run()
{
    // thread starts here
    qDebug() << socketDescriptor << " Starting thread";
    socket = new QTcpSocket();
    if(!socket->setSocketDescriptor(this->socketDescriptor))
    {
        emit error(socket->error());
        return;
    }

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

    qDebug() << socketDescriptor << " Client connected";

    // make this thread a loop
    exec();
}

void MyThread::readyRead()
{
    QByteArray Data = socket->readAll();

    qDebug() << socketDescriptor << " Data in: " << Data;

    socket->write(Data);
}

void MyThread::disconnected()
{
    qDebug() << socketDescriptor << " Disconnected";
    socket->deleteLater();
    exit(0);
}




QTcpServer - ThreadPool


The resulting screen shots of the code are in the picture below.


C++ Tutorial: Sockets - Server & Client using QT - 2015_第2张图片

The picture at the top is a server screen shot. It was listening and then got connected from the client.

Note that whenever there is a new connection, a new thread will be created from the ThreadPool and takes a task from the QRunnable object.

Here is the file used in this section: PoolSvr.tar.gz

Our codes are:

main.cpp

#include <QCoreApplication>
#include "myserver.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    MyServer Server;
    Server.StartServer();

    return a.exec();
}


MyServer header, myserver.h

#ifndef MYSERVER_H
#define MYSERVER_H

#include <QTcpServer>
#include <QThreadPool>
#include <QDebug>
#include "myrunnable.h"

class MyServer : public QTcpServer
{
    Q_OBJECT
public:
    explicit MyServer(QObject *parent = 0);
    void StartServer();

protected:
    void incomingConnection( int handle );
    
signals:
    
public slots:

private:
    QThreadPool *pool;
};

#endif // MYSERVER_H


Implementation file MyServer.cpp

// MyServer.cpp

#include "myserver.h"

MyServer::MyServer(QObject *parent) :
    QTcpServer(parent)
{
    pool = new QThreadPool(this);
    pool->setMaxThreadCount(5);
}

void MyServer::StartServer()
{
    if(this->listen(QHostAddress::Any, 1234))
    {
        qDebug() << "Server started";
    }
    else
    {
        qDebug() << "Server did not start!";
    }
}

void MyServer::incomingConnection(int handle)
{
    MyRunnable *task = new MyRunnable();
    task->setAutoDelete(true);

    task->SocketDescriptor = handle;
    pool->start(task);
}


Runnable header, myrunnable.h

// myrunnable.h

#ifndef MYRUNNABLE_H
#define MYRUNNABLE_H

#include <QRunnable>
#include <QTcpSocket>
#include <QDebug>


class MyRunnable : public QRunnable
{
public:
    MyRunnable();
    int SocketDescriptor;
    
protected:
    void run();
    
};

#endif // MYRUNNABLE_H


The implementation file, MyRunnalbe.cpp

// MyRunnalbe.cpp

#include "myrunnable.h"

MyRunnable::MyRunnable()
{
}

void MyRunnable::run()
{
    if(!SocketDescriptor) return;

    QTcpSocket socket;
    socket.setSocketDescriptor(SocketDescriptor);

    socket.write("hello world");
    socket.flush();
    socket.waitForBytesWritten();
    socket.close();
}




Asynchronous QTcpServer with QThreadPool


The resulting screen shots of the code are in the pictures below.


C++ Tutorial: Sockets - Server & Client using QT - 2015_第3张图片

Server screen shot



C++ Tutorial: Sockets - Server & Client using QT - 2015_第4张图片

Client at 7 screen shot



C++ Tutorial: Sockets - Server & Client using QT - 2015_第5张图片

Client at 8 screen shot


The picture at the top is a server screen shot. It was listening and then got connected from the client.

Note that whenever there is a new connection, a new thread will be created from the ThreadPool and takes a task from the QRunnable object.

The other two pictures are the shots from the client at 7 and 8. At each return key on client window, a new thread created and run the task on the thread which is managed by Qt.

Here is the file used in this section: AsSvr2.tar.gz



main.cpp

#include <QCoreApplication>
#include "myserver.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // Create an instance of a server and then start it.
    MyServer Server;
    Server.StartServer();
    
    return a.exec();
}


Header myserver.h

// myserver.h

#ifndef MYSERVER_H
#define MYSERVER_H

#include <QTcpServer>
#include <QTcpSocket>
#include <QAbstractSocket>
#include "myclient.h"

class MyServer : public QTcpServer
{
    Q_OBJECT
public:
    explicit MyServer(QObject *parent = 0);
    void StartServer();

protected:
    void incomingConnection(int handle);
    
signals:
    
public slots:
    
};

#endif // MYSERVER_H


Implementation file, myserver.cpp

// myserver.cpp

#include "myserver.h"

MyServer::MyServer(QObject *parent) :
    QTcpServer(parent)
{
}

void MyServer::StartServer()
{
    if(listen(QHostAddress::Any, 1234))
    {
        qDebug() << "Server: started";
    }
    else
    {
        qDebug() << "Server: not started!";
    }
}

void MyServer::incomingConnection(int handle)
{
    // at the incoming connection, make a client
    MyClient *client = new MyClient(this);
    client->SetSocket(handle);
}


Client header, myclient.h

// myclient.h

#ifndef MYCLIENT_H
#define MYCLIENT_H

#include <QObject>
#include <QTcpSocket>
#include <QDebug>
#include <QThreadPool>
#include "mytask.h"

class MyClient : public QObject
{
    Q_OBJECT
public:
    explicit MyClient(QObject *parent = 0);
    void SetSocket(int Descriptor);
    
signals:
    
public slots:
    void connected();
    void disconnected();
    void readyRead();

    // make the server fully ascynchronous
    // by doing time consuming task
    void TaskResult(int Number);

private:
    QTcpSocket *socket;
    
};

#endif // MYCLIENT_H


Client implementation file, myclient.cpp

//myclient.cpp

#include "myclient.h"

MyClient::MyClient(QObject *parent) :
    QObject(parent)
{
    QThreadPool::globalInstance()->setMaxThreadCount(5);
}

void MyClient::SetSocket(int Descriptor)
{
    // make a new socket
    socket = new QTcpSocket(this);

    qDebug() << "A new socket created!";

    connect(socket, SIGNAL(connected()), this, SLOT(connected()));
    connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));

    socket->setSocketDescriptor(Descriptor);

    qDebug() << " Client connected at " << Descriptor;
}


// asynchronous - runs separately from the thread we created
void MyClient::connected()
{
    qDebug() << "Client connected event";
}

// asynchronous
void MyClient::disconnected()
{
    qDebug() << "Client disconnected";
}

// Our main thread of execution
// This happening via main thread
// Our code running in our thread not in Qthread
// That's why we're getting the thread from the pool

void MyClient::readyRead()
{
    qDebug() << "MyClient::readyRead()";
    qDebug() << socket->readAll();

    // Time consumer
    MyTask *mytask = new MyTask();
    mytask->setAutoDelete(true);
    connect(mytask, SIGNAL(Result(int)), this, SLOT(TaskResult(int)), Qt::QueuedConnection;)

    qDebug() << "Starting a new task using a thread from the QThreadPool";
    QThreadPool::globalInstance()->start(mytask);

}

// After a task performed a time consuming task.
// We grab the result here, and send it to client
void MyClient::TaskResult(int Number)
{
    QByteArray Buffer;
    Buffer.append("\r\nTask result = ");
    Buffer.append(QString::number(Number));

    socket->write(Buffer);
}


Task header, mytask.h

//mytask.h

#ifndef MYTASK_H
#define MYTASK_H

#include <QRunnable>
#include <QObject>
#include <QRunnable>

// Q_OBJECT missing in the original file generated by class wizard.
// because we set this class with base class QRunnable
// with no inheritance in the class wizard
// We do not have this. So, we cannot use signal/slot
// But we need them.
// Thus, we should use multiple inheritance: QObject inserted here

class MyTask : public QObject, public QRunnable
{
    Q_OBJECT
public:
    MyTask();

signals:
    // notify to the main thread when we're done
    void Result(int Number);

protected:
    void run();
};

#endif // MYTASK_H


Implementation of task, mytask.cpp

#include "mytask.h"
#include <QDebug>


MyTask::MyTask()
{
    qDebug() << "MyTask()";
}

// When the thread pool kicks up, and implements Qrunnable
// it's going to hit this run, and it's going to do this time consuming task.
// After it's done, we're going to send the results back to our main thread.
// This runs in the separate thread, and we do not have any control over this thread,
// but Qt does.
// This may just stay in the queue for several ms depending on how busy the server is.


void MyTask::run()
{
    // time consumer

    qDebug() << "Task start";

    int iNumber = 0;
    for(int i = 0; i < 100; i++)
    {
        iNumber += 1;
    }

    qDebug() << "Task done";
    emit Result(iNumber);
}




Qt5 Note

When we have "QApplication: no such file or directory" error, we now have to add widgets to the QT qmake variable in .pro:

 QT += widgets



For other Qt tutorials, please visit

Qt 5 Tutorial


你可能感兴趣的:(qttcpclient,qttcpserver)