【问题标题】:QTcpSocket::readAll() is emptyQTcpSocket::readAll() 为空
【发布时间】:2017-11-15 14:26:19
【问题描述】:

我是 Qt 新手,有点挣扎。我正在尝试使用 QTcpSocket 将字符串从客户端发送到服务器。

客户端:

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

tSock->connectToHost(ipAddress, portNumb.toInt());
tSock->waitForConnected();
tSock->write(block); // block is not empty
tSock->flush();

服务器端:

void Server::readyRead()
{
    QByteArray block;
    QDataStream out(&block, QIODevice::ReadOnly);
    out << tcpSocket->readAll();
    QString name(block); // block is empty
    players.insert(name, tcpSocket);
    std::cout << "name: " << name.toStdString(); // TODO remove
}

服务器端程序在接收到数据时进入readyRead(),但是block 是空的,虽然在客户端调用tSock-&gt;write(block) 时block 不是空的... 问题出在哪里?我将不胜感激任何帮助。谢谢!

【问题讨论】:

    标签: c++ qt qtcpsocket qtcpserver qdatastream


    【解决方案1】:

    编辑:你的错误是你以ReadOnly模式打开out数据流,但是尝试写入接收到的字节数组给它:

    void Server::readyRead()
    {
        QByteArray block;
        QDataStream out(&block, QIODevice::ReadOnly); // !mistake, WriteOnly mode is needed
        out << tcpSocket->readAll(); // this is write operation
        //...
    }
    

    补充:请注意Serialization mechanism of Qt Data Types在这种情况下很有用:

    tSock->write(block); // this is write just a raw data of the block, not the "QByteArray"
    

    您可以使用流操作将必要的 Qt 数据类型直接写入套接字,而无需转换为QByteArray

    // Connect firstly
    tSock->connectToHost(ipAddress, portNumb.toInt());
    tSock->waitForConnected();
    
    // Then open a data stream for the socket and write to it:
    QDataStream out(tSock, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    out << name; // write string directly without a convertion to QByteArray
    
    // Then you may
    tSock->flush();
    

    在客户端,然后在服务器端使用类似的流操作:

    void Server::readyRead()
    {
        QString name;
        QDataStream in(tcpSocket, QIODevice::ReadOnly /*or QIODevice::ReadWrite if necessary */);
        in.setVersion(QDataStream::Qt_4_0);    
        in >> name; // read the string
        //...
    }
    

    也可以在其他 Qt 的 i/o 设备上读取/写入对象:QFileQSerialPortQProcessQBuffer 等。

    编辑 2: 不能保证在 readyRead 信号上您会收到已发送的完整包裹。因此请参见下面的示例。


    请注意,在实际情况下(当您在客户端-服务器通信中有几个不同的数据包,并且不知道您收到的几个可能的数据包是哪种类型时)通常有使用了更复杂的算法,因为在一次通信的readyRead事件上可能会出现以下几种情况:

    1. 收到完整的数据包
    2. 只收到了部分包裹
    3. 一起收到了几个包裹

    算法的变体(Qt 4 Fortune Client Example):

    void Client::readFortune() // on readyRead
    {
        QDataStream in(tcpSocket);
        in.setVersion(QDataStream::Qt_4_0);
    
        if (blockSize == 0) {
            if (tcpSocket->bytesAvailable() < (int)sizeof(quint16))
                return;
    
            in >> blockSize;
        }
    
        if (tcpSocket->bytesAvailable() < blockSize)
            return;
    
        QString nextFortune;
        in >> nextFortune;    
    
        //...
    }
    

    Qt 4.0 是 Qt 的太旧版本,因此另请参阅 Qt 5.9 Fortune Client Example

    【讨论】:

    • 一般来说,在套接字上重新创建QDataStream 或者是错误的,因为您将丢失流的状态,并且也可能丢失数据。
    • @KubaOber,但这是Qt 4文档中的一个例子,我确信上面readFortune()函数的代码是正确的
    • 目前是正确的,但是一修改就很容易出错。
    【解决方案2】:

    您的服务器似乎没有足够的时间从客户端写入数据。 请试试这个..

    tcpsocket->waitForBytesWritten(1000);
    

    【讨论】:

      【解决方案3】:

      readyRead 代码错误地填写了block。您还假设您将读取足够的数据来传达整个字符串。这是永远无法保证的。当readyRead 触发它时,您所知道的就是至少有一个字节要读取。 QDataStream 交易系统可以帮助解决这个问题。

      这是一种正确的实现方式:

      class Server : public QObject {
        Q_OBJECT
        QPointer<QTcpSocket> m_socket;
        QDataStream m_in{m_socket.data(), &QIODevice::ReadOnly};
        void onReadyRead();
      public:
        Server(QTcpSocket * socket, QObject * parent = {}) :
          QObject(parent),
          m_socket(socket)
        {
          connect(socket, &QIODevice::readyRead, this, &Server::onReadyRead);
        }
      }
      
      void onReadyRead() {
        while (true) { // must keep reading as long as names are available
          QString name;
          in >> name;
          if (in.commitTransaction()) {
            qDebug << name;
            players.insert(name, &m_socket.data());
          } else
            break;
        }
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多