QT

QTcpSocket tcp socket client 프로그래밍 예제.

크게웃기 2019. 2. 18. 15:12
반응형

QTcpSocket tcp socket client 프로그래밍 예제.

시작

 

예전에 C Standard library 를 이용해서 TCP/IP 소켓 프로그래밍을 만들었 적이 있었습니다.

그런데 찾으려고 하니 그 코드가 어디갔는지 알수가 없어서 이번엔 블로그를 남깁니다.

 

QT  App 개발 중 순수 QT lib 을 이용해서 기능을 구현한 것입니다.

 

샘플 코드 첨부는 가장 아랫 쪽에 해 두었습니다.

 

Base 참고 사이트

 https://doc.qt.io/archives/qt-4.8/qt-network-blockingfortuneclient-example.html

 

참고 코드에서 UI부분만 제외한 코드를 건드려 보았습니다.

client 접속부는 thread를 이용할 것이므로, 2개의 파일을 이용하였습니다.

 

1. parent Qobject

2. child QThread

 

우선 .pro 파일에 network lib 를 사용하겠다고 명시를 해야 합니다.

QT += gui network

 

1. Parent QObject

1-1. Header file

#include <QObject>
#include <QMutex>
#include <QDebug>

#include "client.h"

class network : public QObject
{
    Q_OBJECT
public:
    explicit network(QObject *parent = nullptr);

private:
    void requestNewConnection();
    Client client;
    QString currentFortune;
    
    QString ip;
    quint16 port;  

private slots:
    void showFortune(const QString &fortune);
    void Error(int socketError, const QString &message);
}

 

 

1-2. Source File

#include <QMessageBox>

#include "network.h"

network::network(QObject *parent) : QObject(parent)
{
    connect(&c, SIGNAL(newFortune(QString)),
            this, SLOT(showFortune(QString)));
    connect(&c, SIGNAL(error(int,QString)),
            this, SLOT(Error(int,QString)));

    ip = "192.168.1.2";
    port = 6000;

    requestNewConnection();
}

void network::requestNewConnection()
{
    clinet.requestNewConnection(ip, port);
}

void network::showFortune(const QString &nextFortune)
{
    if (nextFortune == currentFortune) {
        requestNewConnection();
        return;
    }

    currentFortune = nextFortune;
}

void network::Error(int socketError, const QString &message)
{
    switch (socketError) {
    case QAbstractSocket::HostNotFoundError:
        break;
    case QAbstractSocket::ConnectionRefusedError:
        break;
    default:
        break;
    }
}

 

상위 코드 requestNewForture event 발생시 서버 접속을 시도 합니다.

 

 

2. Thread

2-1. Header File

#include <QThread>
#include <QMutex>
#include <QtNetwork>
#include <QWaitCondition>

class Client : public QThread
{
    Q_OBJECT
public:
    client(QObject *parent = 0);
    ~client();

    void requestNewConnection(const QString &hostName, quint16 port);
    void run();

signals:
    void newFortune(const QString &fortune);
    void error(int socketError, const QString &message);

private:
    QString hostName;
    quint16 port;
    QMutex mutex;
    QWaitCondition cond;
    bool quit;
};

 

2-2. Source file

#include "Client.h"

Client::Client(QObject *parent)
    : QThread(parent), quit(false)
{
}

Client::~Client()
{
    mutex.lock();
    cond.wakeOne();
    quit = true;
    mutex.unlock();
    wait();
}

void Client::requestNewConnection(const QString &hostName, quint16 port)
{
    QMutexLocker locker(&mutex);
    this->hostName = hostName;
    this->port = port;
    if (!isRunning())
        start();
    else
        cond.wakeOne();
}

void Client::run()
{
    애드블럭을 해제하시면 source code가 보여요
}
#include "Client.h"

Client::Client(QObject *parent)
    : QThread(parent), quit(false)
{
}

Client::~Client()
{
    mutex.lock();
    cond.wakeOne();
    quit = true;
    mutex.unlock();
    wait();
}

void Client::requestNewConnection(const QString &hostName, quint16 port)
{
    QMutexLocker locker(&mutex);
    this->hostName = hostName;
    this->port = port;
    if (!isRunning())
        start();
    else
        cond.wakeOne();
}

void Client::run()
{
    mutex.lock();
    QString serverName = this->hostName;
    quint16 serverPort = this->port;
    mutex.unlock();

    while (!quit) {
        const int Timeout = 20 * 1000;

        QTcpSocket socket;
        socket.connectToHost(serverName, serverPort);

        if (!socket.waitForConnected(Timeout)) {
            emit error(socket.error(), socket.errorString());
            return;
        }

        QByteArray data;
        data.append("A");
        data.append("B");
        data.append("C");
        data.append("E");
        data.append("F");
        socket.write(data);

        if(socket.waitForBytesWritten(Timeout)) {
            emit error(socket.error(), socket.errorString());
            return;
        }

        while (socket.bytesAvailable() < (int)sizeof(quint16)) {
            if (!socket.waitForReadyRead(Timeout)) {
                emit error(socket.error(), socket.errorString());
                return;
            }
            else
            {
                QString s_data = QString::fromAscii(socket.readAll());
                qDebug() << "recv : " << s_data;
            }
        }

        quint16 blockSize;
        QDataStream in(&socket);
        in.setVersion(QDataStream::Qt_4_8);
        in >> blockSize;

        while (socket.bytesAvailable() < blockSize) {
            if (!socket.waitForReadyRead(Timeout)) {
                emit error(socket.error(), socket.errorString());
                return;
            }
        }

        mutex.lock();
        QString fortune;
        in >> fortune;
        emit newFortune(fortune);

        cond.wait(&mutex);
        serverName = hostName;
        serverPort = port;
        mutex.unlock();
    }
}

코드는 위와 같이 끝났습니다. 

그럼 이제 간단한 설명을 하도록 하겠습니다.

이 블로그를 보시는 분들이라면 당연히 위에 코드만 보시더라도 분석이 가능하시겠지만,

QT를 처음하신 분은 도움이 필요 할 수도 있으니 간단히 설명을 해보도록 하겠습니다.

 

Line : 36 
const int Timeout = 20 * 1000;

Timeout 을 설정하는 부분입니다. 단위가 ms 이므로 20초를 지정하였습니다.

 

QTcpSocket socket;

socket.connectToHost(serverName, serverPort);

 

socket 변수 선언 후 해당 socket을 이용하여 IP, Port로 연결을 시도 합니다.

 

if (!socket.waitForConnected(Timeout)) {

    emit error(socket.error(), socket.errorString());

    return;

}

timeout 시간 까지 대기 후 error signal을 발생합니다.

 

QByteArray data;

data.append("A");

data.append("B");

data.append("C");

data.append("E");

data.append("F");

socket.write(data);

 

if(socket.waitForBytesWritten(Timeout)) {

    emit error(socket.error(), socket.errorString());

    return;

}

 

연결 성공시 socket을 이용하여 write를 진행하고, write 실패시 error signal을 발생 합니다.

 

while (socket.bytesAvailable() < (int)sizeof(quint16)) 

{

    if (!socket.waitForReadyRead(Timeout)) {

        emit error(socket.error(), socket.errorString());

        return;

    }else{

        QString s_data = QString::fromAscii(socket.readAll());

        qDebug() << "recv : " << s_data;

    }

}

 

데이터 수신을 기다리며, 데이터 read시 qDebug를 이용하여 수신 받은 데이터를 출력합니다.

 

위의 코드를 파일로 첨부합니다.

필요하신 분은 가져다 사용하시기 바랍니다.

애드블럭 해제시 파일이 나타납니다.

 

 

 

 

 

반응형