如何通过QTcpSocket接收结构?

时间:2016-08-26 13:40:11

标签: qt sockets qtcpsocket

我想使用QTCPSocket发送和接收结构。

系统确实有一个中央套接字服务器和许多Qt客户端应用程序。 每个客户端都发送一个结构,由中央套接字服务器向其余部分广播。

Connector延伸QTcpSocket

这是我的连接方法

void Connector::Open(QString address, int port) // It works!
{
    connectToHost(address, port);
    this->onWaiting();

    connect(this, SIGNAL(connected()), this, SLOT(connected()));
    connect(this, SIGNAL(disconnected()), this, SLOT(disconnected()));
    connect(this, SIGNAL(readyRead()), this, SLOT(readyRead()));
    connect(this, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWritten(qint64)));
}

这是我发送数据的方法

void Connector::SendData(const void* data,qint64 len) // It works!
{
    writeData((char*)data,len);
    waitForBytesWritten();
}

这是为了接收

void Connector::readyRead()  // Don't work
{
    Information n; //My struct 
    memcpy(&n, data.data(), data.size());
    onInformationReceived(n);
}

但收到的信息总是无效的。那是对的吗?

1 个答案:

答案 0 :(得分:1)

存在一些问题:

  1. 可以使用任意数量的可用于读取的字节调用readyRead()槽。你唯一的保证是它超过零字节,尽管在实践中没有理由依赖它。你必须检查bytesAvailable。它可以是一个字节。它可以是兆字节。

    参见例如this answerthat answer用于叙述,that one用于代码。

    至少,你需要:

    void Connector::readyRead() {
      if (bytesAvailable() < sizeof(Information)) return;
      Information info;
      auto const data = read(sizeof(Information));
      memcpy(&info, data.constData(), data.size());
      onInformationReceived(info);
    }
    
  2. 通过网络发送struct作为不透明二进制文件将无效。无法保证接收端将以相同的方式布置结构。除非您参考编译器ABI文档,否则您甚至无法知道数据的二进制格式是什么。

    参见例如this answer了解如何正确完成。

    至少,您需要为QDataStream实施Information流媒体运营商,然后在连接的两端使用它:

    QDataStream & operator<<(QDataStream &, const Information &);
    QDataStream & operator>>(QDataStream &, Information &);
    
    void Connector::send(const Information & info) {
      auto dev = this; // We're a QIODevice :(
      QDataStream stream{dev};
      stream << info;
      dev.waitForBytesWritten(); // this is superbly bad!
    }
    
    void Connector::readyRead() {
      auto dev = this; // We're a QIODevice :(
      QDataStream stream{dev};
      stream.startTransaction();
      Information info;
      dev >> info;
      if (! stream.commitTransaction()) return;
      onInformationReceived(info);
    }
    
  3. 您不应该延长QTcpSocket。您的课程应为QObject,其值为QTcpSocket

  4. 你永远不应该阻止。

  5. 因此,固定的Connector类可能如下所示。同样,this answer介绍了如何正确使用QDataStream来说明与代码的未来更新的兼容性。但另请参阅this answer了解如何利用Qt 5.7&#39; QDataStream中的读取交易。

    class Connector : public QObject {
      Q_OBJECT
      QTcpDevice m_dev;
      QDataStream m_str{&m_dev};
      void onReadyRead() {
        m_str.startTransaction();
        Information info;
        m_str >> info;
        if (! stream.commitTransaction()) return;
        onInformationReceived(info);
      }
      void onBytesWritten() {
        if (m_dev.bytesToWrite() == 0)
          emit allDataSent();
      }
      void onInformationReceived(const Information &);
    public:
      Connector(const QString & address, int port, QObject * parent = nullptr) :
        QObject{parent}
      {
        connect(&m_dev, &QAbstractSocket::connected, this, &Connector::connected);
        connect(&m_dev, &QAbstractSocket::disconnected, this, &Connector::disconnected);
        connect(&m_dev, &QIODevice::readyRead, this, onReadyRead);
        connect(&m_dev, &QIODevice::bytesWritten, this, onBytesWritten);
        m_dev.connect(address, port);
      }
      void send(const Information & info) {
        m_str << info;
      }
      Q_SIGNAL void connected();
      Q_SIGNAL void disconnected();
      Q_SIGNAL void allDataSent();
    }
    
相关问题