Qt: Ejemplo de servidor Fortune

Ejemplo de servidor Fortune

Este artículo es una traducción del documento de ayuda de Qt

Objetivo: crear un servidor para servicios de red.
Este ejemplo se puede ejecutar con el Fortune Clientejemplo o el Blocking Fortune Clientejemplo.

Inserte la descripción de la imagen aquí
Utiliza para QTcpServerrecibir la conexión TCP entrante y utiliza QDataStreamel protocolo de transferencia de datos simple basado en él para escribir fortuna al cliente conectado antes de cerrar la conexión.

class Server : public QDialog
{
    
    
    Q_OBJECT

public:
    explicit Server(QWidget *parent = nullptr);

private slots:
    void sendFortune();

private:
    void initServer();

    QLabel *statusLabel = nullptr;
    QTcpServer *tcpServer = nullptr;
    QVector<QString> fortunes;
};

El servidor se implementa utilizando una clase simple con solo una ranura para manejar las conexiones entrantes.

  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;
      }
      QString ipAddress;
      QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses();
      // use the first non-localhost IPv4 address
      for (int i = 0; i < ipAddressesList.size(); ++i) {
    
    
          if (ipAddressesList.at(i) != QHostAddress::LocalHost &&
              ipAddressesList.at(i).toIPv4Address()) {
    
    
              ipAddress = ipAddressesList.at(i).toString();
              break;
          }
      }
      // if we did not find one, use IPv4 localhost
      if (ipAddress.isEmpty())
          ipAddress = QHostAddress(QHostAddress::LocalHost).toString();
      statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n"
                              "Run the Fortune Client example now.")
                           .arg(ipAddress).arg(tcpServer->serverPort()));

En su constructor, nuestro objeto de servidor llama QTcpServer::listen()para configurar un QTcpServer para escuchar todas las direcciones en cualquier puerto. Luego muestra el puerto QTcpServer seleccionado en la pestaña para que el usuario sepa a qué puerto debe conectarse el cliente de Fortune.

 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.");

Nuestro servidor genera una lista de riqueza aleatoria, que se puede enviar a los clientes conectados.

  connect(tcpServer, &QTcpServer::newConnection, this, &Server::sendFortune);

Cuando el cliente se conecta a nuestro servidor, QTcpServer emitirá QTcpServer :: newConnection (). A su vez, esto llamará a nuestra ranura sendFortune ():

 void Server::sendFortune()
  {
    
    
      QByteArray block;
      QDataStream out(&block, QIODevice::WriteOnly);
      out.setVersion(QDataStream::Qt_5_10);

      out << fortunes[QRandomGenerator::global()->bounded(fortunes.size())];

El propósito de esta ranura es seleccionar aleatoriamente una fila de la lista de suerte, codificarla en un QByteArray usando QDataStream y luego escribirla en el socket conectado. Este es un método común de usar QTcpSocket para transmitir datos binarios. Primero, creamos un objeto QByteArray y QDataStream, y pasamos el bytearray al constructor QDataStream. Luego, establecemos explícitamente la versión del protocolo de QDataStream en QDataStream :: Qt_4_0 para asegurarnos de que podamos comunicarnos con clientes de futuras versiones de Qt (consulte QDataStream :: setVersion ()). Seguimos ingresando riqueza al azar.

 QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
      connect(clientConnection, &QAbstractSocket::disconnected,
              clientConnection, &QObject::deleteLater);

Luego llamamos QTcpServer :: nextPendingConnection (), que devuelve el QTcpSocket que representa el lado del servidor de la conexión. Al conectar QTcpSocket :: desconectado () a QObject :: deleteLater (), podemos asegurarnos de que el socket se elimine después de la desconexión.


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

La riqueza codificada se escribe usando QTcpSocket :: write (), y finalmente llamamos a QTcpSocket :: desconectarFromHost (), que cerrará la conexión después de que QTcpSocket termine de escribir la riqueza en la red. Dado que QTcpSocket funciona de forma asincrónica, los datos se escribirán después de que regrese la función y el control volverá al bucle de eventos de Qt. Luego, el socket se cerrará, lo que hará que QObject :: deleteLater () lo elimine.

Ejemplo de servidor Fortune con subprocesos

Este artículo es una traducción del documento de ayuda de Qt

El ejemplo del servidor Fortune con subprocesos muestra cómo crear un servidor para un servicio web simple que usa subprocesos para manejar solicitudes de diferentes clientes. Se Fortune Clientejecutará con el ejemplo.
Inserte la descripción de la imagen aquí
La implementación de este ejemplo es similar a la del ejemplo de Fortune Server, pero aquí implementaremos una subclase de QTcpServer, que inicia cada conexión en un hilo diferente.

Para esto, necesitamos dos clases: una es la subclase FortuneServer de QTcpServer y la otra es FortuneThread que hereda QThread.


  class FortuneServer : public QTcpServer
  {
    
    
      Q_OBJECT

  public:
      FortuneServer(QObject *parent = 0);

  protected:
      void incomingConnection(qintptr socketDescriptor) override;

  private:
      QStringList fortunes;
  };

FortuneServer hereda QTcpServer y reimplementa QTcpServer :: entranteConnection (). También lo usamos para almacenar listas de destinos aleatorios.

 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.");
  }

Usamos el constructor de FortuneServer para generar la lista de riqueza.

  void FortuneServer::incomingConnection(qintptr socketDescriptor)
  {
    
    
      QString fortune = fortunes.at(QRandomGenerator::global()->bounded(fortunes.size()));
      FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this);
      connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
      thread->start();
  }

Nuestra implementación QTcpServer :: entranteConnection () crea un objeto FortuneThread y pasa el descriptor de socket entrante y la fortuna aleatoria al constructor de FortuneThread. Al conectar la señal de terminado () de FortuneThread a QObject :: deleteLater (), nos aseguramos de que el hilo se eliminará una vez que haya finalizado. Entonces podemos llamar a QThread :: start (), que inicia el hilo.

 class FortuneThread : public QThread
  {
    
    
      Q_OBJECT

  public:
      FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent);

      void run() override;

  signals:
      void error(QTcpSocket::SocketError socketError);

  private:
      int socketDescriptor;
      QString text;
  };

Continúe mirando la clase FortuneThread, esta es una subclase QThread, su trabajo es escribir fortuna en el socket conectado. Esta clase vuelve a implementar QThread :: run () y tiene una señal para informar un error.


  FortuneThread::FortuneThread(int socketDescriptor, const QString &fortune, QObject *parent)
      : QThread(parent), socketDescriptor(socketDescriptor), text(fortune)
  {
    
    
  }

El constructor de FortuneThread simplemente almacena el descriptor de socket y el texto de la fortuna para que run () se pueda usar más tarde.

  void FortuneThread::run()
  {
    
    
      QTcpSocket tcpSocket;

Lo primero que hace la función run () es crear un objeto QTcpSocket en la pila. Vale la pena señalar que creamos este objeto en el hilo y automáticamente asociará el conector con el bucle de eventos del hilo. Esto asegura que cuando accedemos al evento desde fortunthread :: run (), Qt no intentará pasar el evento al socket desde el hilo principal.

  if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
    
    
          emit error(tcpSocket.error());
          return;
      }

Al llamar a QTcpSocket :: setSocketDescriptor (), el descriptor de socket se pasa como un parámetro para inicializar el socket. Esperamos que esto tenga éxito, pero para asegurarnos (aunque es poco probable, es posible que el sistema se quede sin recursos), capturamos el valor de retorno e informamos cualquier error.

 QByteArray block;
      QDataStream out(&block, QIODevice::WriteOnly);
      out.setVersion(QDataStream::Qt_4_0);
      out << text;

Al igual que con el ejemplo del servidor Fortune, usamos QDataStream para codificar Fortune en QByteArray.


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

Pero a diferencia del ejemplo anterior, finalizamos la llamada llamando a QTcpSocket :: waitForDisconnected (), que bloquea el hilo de llamada hasta que se desconecta el socket. Debido a que estamos ejecutando en un hilo separado, la GUI seguirá respondiendo.

Supongo que te gusta

Origin blog.csdn.net/zhizhengguan/article/details/114360199
Recomendado
Clasificación