서론

QTcpServer 클래스와 QTcpSocket 클래스를 이용해 서버/클라이언트를 구현해보자

 

QTcpServer

 

그림과 같이 GUI 상에 위젯을 배치해 보도록 하자.

 

CMAKE 기반 프로젝트는 아래 코드를 추가하자

find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)

target_link_libraries(QTcpServer PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)

 

QMAKE 기반 프로젝트는 아래 코드를 추가하자

QT += network

 

 

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QObject>
#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

 

void initialize() : QTcpServer 클래스의 오브젝트를 초기화를 위한 함수이다.

 

void 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();

    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;
}

 

코드를 살펴보자..

void Widget::initialize(){
    QHostAddress hostAddr;

    // QNetworkInterface : 네트워크 어댑터의 이름, IP 주소, 하드웨어 주소(예: MAC 주소) 등의 정보를 얻을 수 있다.
    QList<QHostAddress> ipList = QNetworkInterface::allAddresses();

    // Use something other than localhost (127.0.0.1)
    for(int i=0; i<ipList.size(); ++i){
        // ipList.at(i) != QHostAddress::LocalHost : 현재 IP 주소가 로컬 호스트 주소(127.0.0.1)가 아닌지 확인
        // ipList.at(i).toIPv4Address() : 현재 IP 주소가 IPv4 주소인지 확인
        if(ipList.at(i) != QHostAddress::LocalHost && ipList.at(i).toIPv4Address()){
            // 조건을 만족하는 경우, hostAddr 변수에 해당 IP 주소를 저장
            hostAddr = ipList.at(i);
            break;
        }
    }

    // 만약 적절한 IP 주소를 찾지 못했다면, hostAddr을 localhost 주소로 설정
    if(hostAddr.toString().isEmpty()){
        hostAddr = QHostAddress(QHostAddress::LocalHost);
    }

    tcpServer = new QTcpServer(this);

    // hostAddr과 포트 25000에서 수신 대기하도록 설정
    // 만약 서버를 시작하지 못하면, 에러 메시지를 표시하고 애플리케이션을 종료
    if (!tcpServer->listen(hostAddr, 25000)){
        QMessageBox::critical(this, tr("TCP Server"), tr("Cannot start the server, Error: %1.").arg(tcpServer->errorString()));
        close();
        return;
    }

    // 서버가 성공적으로 시작되면, labelStatus 위젯에 서버의 IP 주소와 포트 정보를 표시
    ui->labelStatus->setText(tr("Server running \n\n"
                                "IP : %1\n"
                                "PORT : %2\n").arg(hostAddr.toString()).arg(tcpServer->serverPort()));

    // newConnection()시그널을 newConnection()슬롯에 연결하여 새 클라이언트 연결이 있을 때 해당 슬롯이 호출
    connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));

    ui->connMsgEdit->clear();
}
void Widget::newConnection(){

    //새 클라이언트 연결이 있을 때 tcpServer의 nextPendingConnection() 함수를 호출하여 대기 중인 클라이언트 소켓을 가져온다.
    // 이 소켓은 클라이언트와의 통신에 사용
    QTcpSocket *clientConnection = tcpServer->nextPendingConnection();

    // 클라이언트 소켓의 disconnected() 시그널을 소켓의 deleteLater() 슬롯에 연결
    // 이렇게 하면 클라이언트가 연결을 끊을 때 소켓 객체가 자동으로 삭제
    connect(clientConnection, SIGNAL(disconnected()), clientConnection, SLOT(deleteLater()));

    // 현재 시간을 hh:mm:ss 형식으로 가져와서 문자열로 변환합니다.
    QString currTime = QTime::currentTime().toString("hh:mm:ss");

    // Client Connection" 메시지에 현재 시간을 포함시킨 텍스트를 생성
    QString text = QString("Client Connection (%1)").arg(currTime);

    // connMsgEdit 위젯에 이 텍스트를 추가
    // 이는 새 클라이언트 연결이 생길 때마다 UI에 로그를 남기기 위함
    ui->connMsgEdit->append(text);

    QByteArray message = QByteArray("Hello Client ~ ");

    clientConnection->write(message);

    clientConnection->disconnectFromHost();
   }

 

생성자 함수에서 호출되는 initialize() 함수는 클라이언트가 접속할 서버의 IP를 시스템으로부터 얻어온다.

그리고 QTcpServer 클래스의 tcpServer 오브젝트를 초기화 하고 listen() 멤버 함수를 이용해 클라이언트 접속 요청 대기 상태가 될 수 있도록 한다.

listen() 함수의 첫 번째 인자는 IP 주소이며 두 번째 인자는 서버 Port 번호이다.

그리고 initialize( ) 함수 하단의 connect( ) 함수는 클라이언트가 접속하게 되면 호출되 는 Signal 과 Slot 함수를 연결하였다.

따라서 새로운 클라이언트가 접속하게 되면 newConnection( ) 함수가 호출된다.

newConnection( ) 함수는 GUI 상에 QTextEdit 위젯에 클라이언트가 접속한 시간을 출 력하고 접속한 클라이언트에게 “Hello Client ~ “ 메시지를 전송한다.

'🌠Development > QT' 카테고리의 다른 글

QT project - 응용 프로그램 만들어보기  (0) 2024.07.16
QT - TcpSocket 통신 Client  (0) 2024.07.15
QT - 네트워크 프로그래밍, socket  (0) 2024.07.12
QT - Dialog(QInputDialog)  (0) 2024.07.12
QT - Dialog  (0) 2024.07.12