스레드 기반 Fortune 서버
'스레드 기반 Fortune 서버' 예제는 스레드를 사용하여 서로 다른 클라이언트의 요청을 처리하는 간단한 네트워크 서비스용 서버를 만드는 방법을 보여줍니다. 이 예제는 'Fortune 클라이언트' 예제와 함께 실행하도록 설계되었습니다.

이 예제의 구현은 ‘Fortune 서버’ 예제와 유사하지만, 여기서는 각 연결을 서로 다른 스레드에서 시작하는 ` QTcpServer `의 하위 클래스를 구현할 것입니다.
이를 위해 두 개의 클래스가 필요합니다. 하나는 ` QTcpServer `의 서브클래스인 `FortuneServer`이고, 다른 하나는 ` QThread`을 상속받는 `FortuneThread`입니다.
class FortuneServer : public QTcpServer
{
Q_OBJECT
public:
FortuneServer(QObject *parent = nullptr);
protected:
void incomingConnection(qintptr socketDescriptor) override;
private:
QStringList fortunes;
};FortuneServer는 QTcpServer 를 상속받고 QTcpServer::incomingConnection()을 재구현합니다. 또한 이 클래스는 무작위 운세 목록을 저장하는 데에도 사용됩니다.
FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{
fortunes << tr("You've been leading a dog's life. Stay off the furniture.")
<< tr("You've got to think about tomorrow.")
<< tr("You will be surprised by a loud noise.")
<< tr("You will feel hungry again in another hour.")
<< tr("You might have mail.")
<< tr("You cannot kill time without injuring eternity.")
<< tr("Computers are not intelligent. They only think they are.");
}FortuneServer의 생성자를 사용하여 운세 목록을 간단히 생성합니다.
void FortuneServer::incomingConnection(qintptr socketDescriptor)
{
QString fortune = fortunes.at(QRandomGenerator::global()->bounded(fortunes.size()));
FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this);
connect(thread, &FortuneThread::finished, thread, &FortuneThread::deleteLater);
thread->start();
}QTcpServer::incomingConnection() 메서드의 구현에서는 들어오는 소켓 디스크립터와 무작위 운세 하나를 FortuneThread의 생성자에 전달하여 FortuneThread 객체를 생성합니다. FortuneThread의 finished() 신호를 QObject::deleteLater()에 연결함으로써, 스레드가 작업을 완료하면 삭제되도록 보장합니다. 그런 다음 QThread::start()를 호출하여 스레드를 시작합니다.
class FortuneThread : public QThread
{
Q_OBJECT
public:
FortuneThread(qintptr socketDescriptor, const QString &fortune, QObject *parent);
void run() override;
signals:
void error(QTcpSocket::SocketError socketError);
private:
qintptr socketDescriptor;
QString text;
};FortuneThread 클래스로 넘어가 보면, 이 클래스는 QThread 의 서브클래스로, 연결된 소켓에 운세 메시지를 쓰는 역할을 합니다. 이 클래스는 QThread::run()을 재구현하며, 오류를 보고하기 위한 신호를 가지고 있습니다.
FortuneThread::FortuneThread(qintptr socketDescriptor, const QString &fortune, QObject *parent)
: QThread(parent), socketDescriptor(socketDescriptor), text(fortune)
{
}FortuneThread의 생성자는 소켓 디스크립터와 운세 텍스트를 저장하기만 하며, 이는 나중에 run()에서 사용할 수 있도록 하기 위함입니다.
void FortuneThread::run()
{
QTcpSocket tcpSocket;run() 함수가 가장 먼저 수행하는 작업은 스택에 QTcpSocket 객체를 생성하는 것입니다. 주목할 점은 이 객체를 스레드 내부에서 생성한다는 것입니다. 이렇게 하면 소켓이 자동으로 해당 스레드의 이벤트 루프에 연결됩니다. 이를 통해 FortuneThread::run()에서 소켓에 접근하는 동안 Qt가 메인 스레드에서 해당 소켓으로 이벤트를 전달하려고 시도하지 않게 됩니다.
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocket.error());
return;
}소켓은 소켓 디스크립터를 인수로 전달하여 QTcpSocket::setSocketDescriptor()를 호출함으로써 초기화됩니다. 이 작업이 성공할 것으로 예상되지만, 만일을 대비하여(비록 가능성이 낮더라도 시스템 자원이 부족해질 수 있으므로) 반환 값을 확인하여 오류가 발생하면 이를 보고합니다.
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_6_5);
out << text;Fortune Server 예제와 마찬가지로, ` QDataStream`를 사용하여 운세를 ` QByteArray `로 인코딩합니다.
tcpSocket.write(block);
tcpSocket.disconnectFromHost();
tcpSocket.waitForDisconnected();
}하지만 이전 예제와 달리, 소켓 연결이 끊어질 때까지 호출 스레드를 차단하는 ` QTcpSocket::waitForDisconnected()`를 호출하여 처리를 마무리합니다. 별도의 스레드에서 실행되므로 GUI는 계속 반응성을 유지합니다.
관련 항목: Fortune 서버, Fortune 클라이언트, 차단형 Fortune 클라이언트.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.