01 void TripPlanner::stopSearch()
02 {
03 statusLabel->setText(tr("Search stopped"));
04 closeConnection();
05 }
Слот stopSearch() подсоединяется к сигналу clicked() кнопки Stop. По существу, он просто вызывает функцию closeConnection().
01 void TripPlanner::connectionClosedByServer()
02 {
03 if (nextBlockSize != 0xFFFF)
04 statusLabel->setText(tr("Error: Connection closed by server" ));
05 closeConnection();
06 }
Слот connectionClosedByServer() подсоединяется к сигналу disconnected() объекта QTcpSocket. Если сервер закрывает соединение и мы еще не получили маркер конца, мы уведомляем пользователя о возникновении ошибки. И как обычно, мы вызываем функцию closeConnection() для обновления интерфейса пользователя.
01 void TripPlanner::error()
02 {
03 statusLabel->setText(tcpSocket.errorString());
04 closeConnection();
05 }
Слот error() подсоединяется к сигналу error(QAbstractSocket::SocketError) объекта QTcpSocket. Мы игнорируем код ошибки и используем функцию QTcpSocket::errorString(), которая возвращает понятное человеку сообщение о последней возникшей ошибке.
На этом завершается рассмотрение класса TripPlanner. Функция main() приложения Trip Planner выглядит обычным образом:
01 int main(int argc, char *argv[])
02 {
03 QApplication app(argc, argv);
04 TripPlanner tripPlanner;
05 tripPlanner.show();
06 return app.exec();
07 }
Теперь давайте реализуем сервер. Сервер состоит из двух классов: TripServer и ClientSocket. Класс TripServer наследует QTcpServer — класс, который позволяет нам принимать входящие соединения TCP. Класс ClientSocket переопределяет QTcpSocket и обслуживает одно соединение. В каждый момент времени в памяти имеется ровно столько объектов типа ClientSocket, сколько обслуживается клиентов.
01 class TripServer : public QTcpServer
02 {
03 Q_OBJECT
04 public:
05 TripServer(QObject *parent = 0);
06 private:
07 void incomingConnection(int socketId);
08 };
Класс TripServer переопределяет функцию incomingConnection() из класса QTcpServer. Данная функция вызывается всякий раз, когда клиент пытается подсоединиться к порту, который прослушивает сервер.
01 TripServer::TripServer(QObject *parent)
02 : QTcpServer (parent)
03 {
04 }
Конструктор TripServer тривиален.
01 void TripServer::incomingConnection(int socketId)
02 {
03 ClientSocket *socket = new ClientSocket(this);
04 socket->setSocketDescriptor(socketId);
05 }
В функции incomingConnection() мы создаем объект ClientSocket в качестве дочернего по отношению к объекту TripServer, и мы устанавливаем дескриптор его coкета на переданное нам значение. Объект ClientSocket автоматически удалит сам себя при прекращении соединения.
01 class ClientSocket : public QTcpSocket
02 {
03 Q_OBJECT
04 public:
05 ClientSocket(QObject *parent = 0);
06 private slots:
07 void readClient();
08 private:
09 void generateRandomTrip(const QString &from, const QString &to,
10 const QDate &date, const QTime &time);
11 quint16 nextBlockSize;
12 };
Класс ClientSocket наследует QTcpSocket и инкапсулирует состояние одного клиента.
01 ClientSocket::ClientSocket(QObject *parent)
02 : QTcpSocket(parent)
03 {
04 connect(this, SIGNAL(readyRead()), this, SLOT(readClient()));
05 connect(this, SIGNAL(disconnected()), this, SLOT(deleteLater()));
06 nextBlockSize = 0;
07 }
В конструкторе мы устанавливаем необходимые соединения сигнал—слот и задаем переменной nextBlockSize значение 0, свидетельствующее о том, что мы еще не знаем размер посланного клиентом блока.
Сигнал disconnected() подсоединяется к функции deleteLater(), которая наследуется от класса QObject, и удаляет объект после возврата управления в цикл обработки событий Qt. Это обеспечивает удаление объекта ClientSocket после закрытия сокетного соединения.
01 void ClientSocket::readClient()
02 {
03 QDataStream in(this);
04 in.setVersion(QDataStream::Qt_4_1);
05 if (nextBlockSize == 0) {
06 if (bytesAvailable() < sizeof(quint16))
07 return;
08 in >> nextBlockSize;
09 }
10 if (bytesAvailable() < nextBlockSize)
11 return;