Home · All Classes · Main Classes · Grouped Classes · Modules · Functions

[Предыдущая: Поддержка GUI Баз Данных в Qt 4] [Начало] [Следующая: API Стиля в Qt 4]

Модуль Работы с Сетью в Qt 4

Модуль работы с сетью в Qt 4 предоставляет некоторые новые возможности, такие как поддержка интернационализованных доменных имен, улучшенная поддержка IPv6 и более удобная работа. И так как Qt 4 позволило нам нарушить совместимость с предыдущими версиями, Мы сделали более интуитивно-понятными имена классов и API для более удобного использования.

Общий Обзор

По сравнению с Qt 3, модуль работы с сетью в Qt 4 предлагает следующие преимущества:

Модуль работы с сетью Qt 4 предоставляет базовые классы для посланий TCP и приложений UDP, а также высокоуровневые классы для реализации клиентской стороны протоколов HTTP и FTP.

Далее идет краткий обзор классов TCP и UDP:

QTcpSocket и QUdpSocket наследуют большинство своих функциональных возможностей от QAbstractSocket. Вы также можете использовать QAbstractSocket как оболочку родной модели сокета.

По умолчанию классы сокетов работают асинхронно (т.е. без блокировки) испуская сигналы-уведомления лишь когда поступают данные или когда собеседник закрывает связь. В многопоточных приложениях или в не-GUI приложениях, Вы также можете использовать блокирующие (синхронизирующие) слот функции, что позволяет составлять более простые программы, со сконцентрированной в двух функциях логикой работы с сетью вместо размазанной по множеству слотов.

Для реализации протоколов FTP и HTTP QFtp и QHttp внутри используют QTcpSocket. Оба класса работают асинхронно и могут планировать (т.е. ставить в очередь) запросы.

Модуль работы в сети содержит четыре вспомогательных класса: QHostAddress, QHostInfo, QUrl и QUrlInfo. QHostAddress содержит адреса IPv4 или IPv6, QHostInfo содержит имя хоста в адресе, QUrl содержит URL, а QUrlInfo содержит информацию о ресурсе указанном в URL, такую как размер файла и дату модификации. (Поскольку QUrl используется QTextBrowser, то он является частью библиотеки QtCore, а не QtNetwork.)

Для получения более подробной информации см. обзор модуля QtNetwork.

Примеры Кода

Все приведенные здесь отрывки кода взяты из примеров программ расположенных в директории Qt examples/network.

Клиент TCP

Первый пример показывает, как можно написать клиент TCP с использованием QTcpSocket. Клиент общается с сервером удачи, который предсказывает удачу пользователя. Здесь показано, как установить сокет:

        tcpSocket = new QTcpSocket(this);

        connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readFortune()));
        connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(displayError(QAbstractSocket::SocketError)));

Когда клиент делает новый запрос удачи, клиент устанавливает связь с сервером:

        tcpSocket->connectToHost(hostLineEdit->text(),
                                 portLineEdit->text().toInt());

Следующий код читает данные из сокета при ответе сервера:

        QDataStream in(tcpSocket);
        in.setVersion(QDataStream::Qt_4_0);

        if (blockSize == 0) {
            if (tcpSocket->bytesAvailable() < (int)sizeof(quint16))
                return;

            in >> blockSize;
        }

        if (tcpSocket->bytesAvailable() < blockSize)
            return;

        QString nextFortune;
        in >> nextFortune;

        if (nextFortune == currentFortune) {
            QTimer::singleShot(0, this, SLOT(requestNewFortune()));
            return;
        }

        currentFortune = nextFortune;

Ответ сервера начинается с размера сообщения (который мы сохраняем в blockSize), за size следуют байты данных. Если клиент получил не все данные, то ждет, пока сервер пришлет еще.

Альтернативный подход состоит в том, чтобы блокировать слот. Весь код тогда может быть помещен в одной функции:

            const int Timeout = 5 * 1000;

            QTcpSocket socket;
            socket.connectToHost(serverName, serverPort);

            if (!socket.waitForConnected(Timeout)) {
                emit error(socket.error(), socket.errorString());
                return;
            }

            while (socket.bytesAvailable() < (int)sizeof(quint16)) {
                if (!socket.waitForReadyRead(Timeout)) {
                    emit error(socket.error(), socket.errorString());
                    return;
                }
            }

            quint16 blockSize;
            QDataStream in(&socket);
            in.setVersion(QDataStream::Qt_4_0);
            in >> blockSize;

            while (socket.bytesAvailable() < blockSize) {
                if (!socket.waitForReadyRead(Timeout)) {
                    emit error(socket.error(), socket.errorString());
                    return;
                }
            }

            QMutexLocker locker(&mutex);

            QString fortune;
            in >> fortune;
            emit newFortune(fortune);

Сервер TCP

Следующие отрывки кода иллюстрируют, как написать сервер TCP с использованием QTcpServer и QTcpSocket. Здесь показано как запустить сервер TCP:

        tcpServer = new QTcpServer(this);
        if (!tcpServer->listen()) {
            QMessageBox::critical(this, tr("Fortune Server"),
                                  tr("Unable to start the server: %1.")
                                  .arg(tcpServer->errorString()));
            close();
            return;
        }

        connect(tcpServer, SIGNAL(newConnection()), this, SLOT(sendFortune()));

Когда клиент пытается соединиться с сервером, выполняется следующий код в слоте sendFortune():

        QByteArray block;
        QDataStream out(&block, QIODevice::WriteOnly);
        out.setVersion(QDataStream::Qt_4_0);
        out << (quint16)0;
        out << fortunes.at(rand() % fortunes.size());
        out.device()->seek(0);
        out << (quint16)(block.size() - sizeof(quint16));

        QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
        connect(clientConnection, SIGNAL(disconnected()),
                clientConnection, SLOT(deleteLater()));

        clientConnection->write(block);
        clientConnection->disconnectFromHost();

Отправители и Получатели UDP

Здесь показано, как передать датаграмму UDP:

        udpSocket = new QUdpSocket(this);
        QByteArray datagram = "Broadcast message " + QByteArray::number(messageNo);
        udpSocket->writeDatagram(datagram.data(), datagram.size(),
                                 QHostAddress::Broadcast, 45454);

Здесь показано, как принять датаграмму UDP:

        udpSocket = new QUdpSocket(this);
        udpSocket->bind(45454);

        connect(udpSocket, SIGNAL(readyRead()),
                this, SLOT(processPendingDatagrams()));

Тогда в слоте processPendingDatagrams() следует написать:

        while (udpSocket->hasPendingDatagrams()) {
            QByteArray datagram;
            datagram.resize(udpSocket->pendingDatagramSize());
            udpSocket->readDatagram(datagram.data(), datagram.size());
            statusLabel->setText(tr("Received datagram: \"%1\"")
                                 .arg(datagram.data()));
        }

Сравнение с Qt 3

Главное различие между Qt 3 и Qt 4 в том, что был устранет самый высокий уровень абстракции - QNetworkProtocol и QUrlOperator. Данные классы были попыткой сделать невозможное (объединить FTP и HTTP под одной крышей), и неудивительно, что это не удалось. Qt 4 все еще предоставляет классы QFtp и QHttp, но только с большим количеством продуманных API-функций, которые появились в Qt 3.1.

Класс Qt 3 QSocket был переименован в QTcpSocket. Новый класс поддерживает блокировки. Также более легко обработать закрытие соединения, чем в Qt 3, там Вы должны были соединить сигналы QSocket::connectionClosed() и QSocket::delayedCloseFinished().

Класс Qt 3 QServerSocket был переименован в QTcpServer. API изменился незначительно. В то время как в Qt 3 было необходимо создание подкласса QServerSocket и переопределение чисто виртуальной функции newConnection(), QTcpServer испускает сигнал newConnection(), который можно соединить со слотом.

Класс QHostInfo был перепроектирован для того, чтобы использовать функцию операционной системы getaddrinfo() вместо реализации протокола DNS. Внутри себя QHostInfo просто создает новый поток и в этом потоке вызывает getaddrinfo(). Это было невозможно в Qt 3 потому, что getaddrinfo() вызывает блокировку, а Qt 3 был создан без поддержки многопоточности.

Класс Qt 3 QSocketDevice больше не является частью открытого API Qt. Если Вы привыкли использовать QSocketDevice для посылки и приема датаграмм UDP, то используйте вместо него QUdpSocket. Если Вы использовали QSocketDevice потому, что он поддерживал блокировку сокетов, то используйте QTcpSocket или QUdpSocket и функции блокировки (waitForConnected(), waitForReadyRead() и т.д.) вместо него. Если Вы использовали QSocketDevice от не-GUI потока, т.к. это единственный монопоточный класс организации сети в Qt 3, то вместо него используйте QTcpSocket, QTcpServer или QUdpSocket.

Внутри Qt 4 имеет класс с именем QSocketLayer, который предоставляет кроссплатформенный низкоуровневый API для работы с сокетом. Он напоминает старый класс QSocketDevice. Если пользователи попросят, мы можем сделать его открытым в следующих релизах.

Для оказания помощи в переходе на Qt 4 библиотека Qt3Support включает в себя классы Q3Dns, Q3ServerSocket, Q3Socket и Q3SocketDevice.

[Предыдущая: Поддержка GUI Баз Данных в Qt 4] [Начало] [Следующая: API Стиля в Qt 4]


Copyright © 2005 Trolltech Trademarks
Qt 4.1.0
Hosted by uCoz