Qt프로그램

TCP 프로토콜 기반 서버/클라이언트 접속구현

Barbarian developer 2024. 10. 10.

QTcpServer 클래스를 이용한 TCP서버 예제

CMake 기반 프로젝트 생성 후, CMakeLists.txt 파일에 아래와 같이 항목을 수정 및 추가한다.

find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
...
target_link_libraries(00_TcpServer PRIVATE Qt${QT_VERSION_MAJOR}::Network)

 

find_package 두줄은 set(CMAKE_CXX_STANDARD_REQUIRED ON) 아래 추가하면되고,

target_link는 widget을 인자로 삼은target_link코드 아래 추가하면 된다.

 

<widget.h>

#ifndef WIDGET_H
#define WIDGET_H

#include <QObject>
#include <QWidget>

#include <QtNetwork/QTcpServer>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
    QTcpServer *tcpServer;

    void initialize();

private slots:
    void newConnection();

};
#endif // WIDGET_H
  • initialize( ) 함수는 QTcpServer 클래스의 오브젝트를 초기화를위한 함수이다.
  • newConnection( ) Slot 함수는 클라이언트가 접속하면 Signal이 발생하면 호출되는 함수

<widget.cpp>

#include "widget.h"
#include "./ui_widget.h"

#include <QtNetwork/QNetworkInterface>
#include <QtNetwork/QTcpSocket>
#include <QMessageBox>
#include <QTime>

Widget::Widget(QWidget *parent)
	: QWidget(parent)
	, ui(new Ui::Widget)
{
    ui->setupUi(this);
    initialize();
}

void Widget::initialize()
{
    QHostAddress hostAddr;
    QList<QHostAddress> ipList = QNetworkInterface::allAddresses();

    // Use something other than localhost (127.0.0.1)
    for (int i = 0; i < ipList.size(); ++i) {
        if (ipList.at(i) != QHostAddress::LocalHost &&
            ipList.at(i).toIPv4Address()) {
            hostAddr = ipList.at(i);
            break;
            }
        }

	if (hostAddr.toString().isEmpty())
  	  hostAddr = QHostAddress(QHostAddress::LocalHost);
	tcpServer = new QTcpServer(this);
    if (!tcpServer->listen(hostAddr, 25000)) {
        QMessageBox::critical(this, tr("TCP Server"),
                                    tr("Cannot start the server, Error: %1.")
                                    .arg(tcpServer->errorString()));
        close();
        return;
    }
    ui->labelStatus->setText(tr("Server running \n\n"
                                "IP : %1\n"
                                "PORT : %2\n")
                                .arg(hostAddr.toString())
                                .arg(tcpServer->serverPort()));
                                
    connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
    
    ui->connMsgEdit->clear();
}

void Widget::newConnection()
{
    QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
    connect(clientConnection, SIGNAL(disconnected()),
            clientConnection, SLOT(deleteLater()));
            
    QString currTime = QTime::currentTime().toString("hh:mm:ss");
    QString text = QString("Client Connection (%1)").arg(currTime);
    
                   ui->connMsgEdit->append(text);
    QByteArray message = QByteArray("Hello Client ~ ");
    clientConnection->write(message);
    clientConnection->disconnectFromHost();
}

Widget::~Widget()
{
	delete ui;
}
  • 생성자 함수에서 호출되는 initialize( ) 함수는 클라이언트가 접속할 서버의 IP를 시스템으로부터 얻어온다.
  • QTcpServer 클래스의 tcpServer 오브젝트를 초기화한다.
  • listen( ) 멤버 함수를 이용해 클라이언트 접속 요청 대기 상태가 될 수 있도록 한다.
  • listen( ) 함수의 첫 번째 인자는 IP주소이며 두 번째 인자는 서버 Port 번호이다.
  • 그리고 initialize( ) 함수 하단의 connect( ) 함수는 클라이언트가 접속하게 되면 호출되는 Signal 과 Slot 함수를 연결하였다.
  • 새로운 클라이언트가 접속하게 되면 newConnection( ) 함수가 호출된다.
  • 새로운 클라이언트가 접속하게 되면 newConnection( ) 함수가 호출된다.

 

QTcpSocket 클래스를 이용한 TCP 클라이언트 예제

서버 예제와 동일하게, CMake 기반 프로젝트 생성 후, CMakeLists.txt 파일에 아래와 같이 항목을 수정 및 추가한다.

find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
...
target_link_libraries(00_TcpServer PRIVATE Qt${QT_VERSION_MAJOR}::Network)

 

<widget.h>

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QtNetwork/QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
    QTcpSocket *tcpSocket;

	void initialize();

private slots:
    void connectButton();
    void readMessage(); // Called when receiving a message from the server
    void disconnected();
};
#endif // WIDGET_H
  • connectButton( ) Slot 함수는 [접속] 버튼 클릭 시 호출된다.
  • readMessage( ) Slot 함수는 서버로부터 메시지를 받는 Signal 이 발생하면 호출되는 함수이다.

<widget.cpp>

#include "widget.h"
#include "./ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->connectButton, SIGNAL(clicked()),
            this,              SLOT(connectButton()));

    initialize();
}

void Widget::initialize()
{
    tcpSocket = new QTcpSocket(this);

    connect(tcpSocket, SIGNAL(readyRead()),
            this,      SLOT(readMessage()));
    connect(tcpSocket, SIGNAL(disconnected()),
            this,      SLOT(disconnected()));
}

void Widget::connectButton()
{
    QString serverip   = ui->serverIP->text().trimmed();

    QHostAddress serverAddress(serverip);
    tcpSocket->connectToHost(serverAddress, 25000);
}

void Widget::readMessage()
{
    if(tcpSocket->bytesAvailable() >= 0)
    {
        QByteArray readData = tcpSocket->readAll();
        ui->textEdit->append(readData);
    }
}

void Widget::disconnected()
{
    qDebug() << Q_FUNC_INFO << "Server Connection Close.";
}

Widget::~Widget()
{
    delete ui;
}

 

  • initialize( ) 함수는 QTcpSocket 클래스의 tcpSocket 오브젝트를 선언하고 서버로부터 메시지를 받을 발생한 Signal 을 readMessage( ) Slot 함수와 연결한다.
  • 서버와 연결 종료 Signal을 받으면 disconnected( ) Slot 함수와 연결한다.
  • 서버로부터 메시지를 받으면 readMessage( ) 함수를 호출하고 서버와 연결이 종료되면 disconnect( )Slot 함수가 호출된다.
  • readMessage( ) Slot 함수에서 tcpSocket->bytesAvailable() 함수는 서버가 보내온 메시지의 Bytes 수를 구할 수 있다. 그리고 readAll( ) 멤버 함수는 서버가 보내온 메시지를 읽어올 수 있는 기능을 제공한다.

'Qt프로그램' 카테고리의 다른 글

채팅 서버/클라이언트 구현  (0) 2024.10.11
TCP 프로토콜 / 동기 방식 비 동기 방식 구현  (0) 2024.10.10
Qt데이터베이스 모듈  (2) 2024.10.09
Model and View  (1) 2024.10.09
Container Classes  (1) 2024.10.08

댓글