서론
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 |