Qt:Fortune Server Example

Fortune Server Example

此文为Qt帮助文档的翻译

目的:为网络服务创建服务器
本例可以与Fortune Client示例或Blocking Fortune Client示例一起运行。

在这里插入图片描述
它使用QTcpServer接收传入的TCP连接,并使用基于QDataStream的简单数据传输协议在关闭连接之前将fortune 写入连接的客户端

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;
};

服务器是使用一个只有一个插槽的简单类来实现的,用于处理传入的连接。

  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()));

在其构造函数中,我们的服务器对象调用QTcpServer::listen()来设置一个QTcpServer来监听任意端口上的所有地址。In然后显示标签中选择的端口QTcpServer,以便用户知道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.");

我们的服务器生成一个随机财富列表,它可以发送到连接的客户端。

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

当客户端连接到我们的服务器时,QTcpServer将发出QTcpServer::newConnection()。反过来,这将调用我们的sendFortune()槽:

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

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

这个槽的目的是从运气列表中随机选择一行,使用QDataStream将其编码为QByteArray,然后将其写入连接的套接字。这是使用QTcpSocket传输二进制数据的常用方法。首先,我们创建一个QByteArray和一个QDataStream对象,将bytearray传递给QDataStream的构造函数。然后我们显式地设置QDataStream的协议版本为QDataStream::Qt_4_0,以确保我们可以与未来版本的Qt的客户端通信(参见QDataStream::setVersion())。我们继续随机输入财富。

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

然后我们调用QTcpServer::nextPendingConnection(),它返回表示连接的服务器端的QTcpSocket。通过将QTcpSocket::disconnected()连接到QObject::deleteLater(),我们可以确保socket在断开连接后被删除。


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

编码后的财富是使用QTcpSocket::write()编写的,最后我们调用QTcpSocket::disconnectFromHost(),它将在QTcpSocket完成将财富写入网络后关闭连接。由于QTcpSocket是异步工作的,数据将在函数返回后写入,控制返回到Qt的事件循环。然后套接字将关闭,这将导致QObject::deleteLater()删除它。

Threaded Fortune Server Example

此文为Qt帮助文档的翻译

线程Fortune服务器示例展示了如何为一个简单的网络服务创建一个服务器,该服务器使用线程来处理来自不同客户机的请求。它将与Fortune Client范例一起运行。
在这里插入图片描述
此示例的实现与Fortune Server example的实现类似,但这里我们将实现QTcpServer的一个子类,它在不同的线程中启动每个连接。

为此,我们需要两个类:一个是QTcpServer子类FortuneServer,另一个是继承QThread的FortuneThread。


  class FortuneServer : public QTcpServer
  {
    
    
      Q_OBJECT

  public:
      FortuneServer(QObject *parent = 0);

  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, SIGNAL(finished()), thread, SLOT(deleteLater()));
      thread->start();
  }

我们的QTcpServer::incomingConnection()实现创建了一个FortuneThread对象,将传入的套接字描述符和随机的fortune传递给FortuneThread的构造函数。通过将FortuneThread的finished()信号连接到QObject::deleteLater(),我们确保线程一旦完成就会被删除。然后我们可以调用QThread::start(),它启动线程。

 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;
  };

继续看FortuneThread类,这是一个QThread子类,它的工作是将fortune写到连接的套接字中。这个类重新实现了QThread::run(),并且它有一个报告错误的信号。


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

FortuneThread的构造函数只是存储套接字描述符和fortune文本,以便稍后可以使用run()。

  void FortuneThread::run()
  {
    
    
      QTcpSocket tcpSocket;

run()函数所做的第一件事是在堆栈上创建一个QTcpSocket对象。值得注意的是,我们是在线程中创建这个对象的,它会自动将套接字与线程的事件循环关联起来。这确保了当我们从fortunthread::run()访问事件时,Qt不会试图从主线程将事件传递给套接字。

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

通过调用QTcpSocket::setSocketDescriptor(),将套接字描述符作为参数传递,从而初始化套接字。我们希望这能成功,但为了确保(虽然不太可能,系统可能会耗尽资源),我们捕获返回值并报告任何错误。

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

与Fortune服务器示例一样,我们使用QDataStream将Fortune编码到QByteArray中。


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

但与前面的示例不同的是,我们通过调用QTcpSocket::waitForDisconnected()来结束调用,它阻塞调用线程,直到套接字断开连接。因为我们在一个单独的线程中运行,GUI将保持响应。

猜你喜欢

转载自blog.csdn.net/zhizhengguan/article/details/114360199