스레드 포춘 서버

스레드 포춘 서버 예제는 스레드를 사용하여 여러 클라이언트의 요청을 처리하는 간단한 네트워크 서비스용 서버를 만드는 방법을 보여줍니다. 이 예제는 Fortune 클라이언트 예제와 함께 실행하기 위한 것입니다.

이 예제의 구현은 Fortune Server 예제와 유사하지만 여기서는 각 연결을 다른 스레드에서 시작하는 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;

포춘 서버 예제에서와 마찬가지로 QDataStream 을 사용하여 포춘을 QByteArray 으로 인코딩합니다.

    tcpSocket.write(block);
    tcpSocket.disconnectFromHost();
    tcpSocket.waitForDisconnected();
}

하지만 이전 예제와 달리 QTcpSocket::waitForDisconnected()를 호출하여 소켓 연결이 끊어질 때까지 호출 스레드를 차단하는 것으로 마무리합니다. 별도의 스레드에서 실행되므로 GUI는 계속 반응합니다.

예제 프로젝트 @ code.qt.io

포춘 서버, 포춘 클라이언트포춘 클라이언트 차단도참조하세요 .

© 2025 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.