подсоединяется к сигналу readyRead класса QTcpSocket. Если nextBlockSize равен 0, мы начинаем считывать размер блока; в противном случае он уже считан нами, и тогда мы проверяем поступление целого блока. Если это целый блок, мы считываем его за один шаг. Мы используем QDataStream непосредственно для QTcpSocket (объект this) и считываем поля, используя оператор >>.
После чтения запроса клиента мы готовы сформировать ответ. В реальном приложении мы осуществляли бы поиск информации в базе данных расписания железнодорожных рейсов и попытались бы найти подходящие рейсы. Но здесь мы воспользуемся функцией generateRandomTrip, которая случайным образом генерирует произвольный рейс. Мы вызываем эту функцию произвольное число раз и затем посылаем 0xFFFF для обозначения конца данных. В конце мы закрываем соединение.
01 void ClientSocket::generateRandomTrip(const QString & /* откуда */,
08 out << quint16(0) << date << time << duration << quint8(1)
09 << QString("InterCity");
10 out.device->seek(0);
11 out << quint16(block.size - sizeof(quint16));
12 write(block);
13 }
Функция generateRandomTrip
демонстрирует способ пересылки блока данных через соединение TCP. Это очень напоминает то, что мы делали в клиенте в функции sendRequest. И вновь мы записываем блок в массив QByteArray таким образом, что мы можем определять его размер до того, как мы его отошлем с помощью функции write.
В функции main мы создаем объект TripServer и кнопку QPushButton, которая позволяет пользователю остановить сервер. Работа сервера начинается с вызова функции QTcpSocket::listen, принимающей адрес IP и номер порта, по которому мы хотим принимать соединения. Специальный адрес 0.0.0.0 (QHostAddress::Any) соответствует наличию любого интерфейса IP на локальном хосте.
Этим завершается наш пример системы клиент—сервер. В данном случае нами использовался блокоориентированный протокол, позволяющий применять объект типа QDataStream для чтения и записи данных. Если бы мы захотели использовать строкоориентированный протокол, наиболее простым было бы применение функций canReadLine и readLine класса QTcpSocket в слоте, подсоединенном к сигналу readyRead:
QStringList lines;
while (tcpSocket.canReadLine)
lines.append(tcpSocket.readLine);
Мы бы затем могли обрабатывать каждую считанную строку. Пересылка данных могла бы выполняться с использованием QTextStream для QTcpSocket.
Представленная здесь реализация сервера не очень эффективна в случае, когда соединений много. Это объясняется тем, что при обработке нами одного запроса мы не обслуживаем другие соединения. Более эффективным был бы запуск нового процесса для каждого соединения. Пример Threaded Fortune Server (многопоточный сервер, передающий клиентам интересные изречения, называемые «fortunes»), расположенный в каталоге Qt examples/network/threadedfortuneserver, демонстрирует, как это можно сделать.