【问题标题】:QTcpSocket readyRead() Signal emitted twiceQTcpSocket readyRead() 信号发出两次
【发布时间】:2018-03-24 06:08:24
【问题描述】:

我有 QTcpServer。我想从客户端发送大数据以及如何在服务器上接收到所有数据时捕获信号? “while (socket->bytesavailable)”不起作用。

例如:

当 qbytearray 大小为 9000 时,它是从客户端发送的,在服务器上它是 4000 或 5000...

示例二:

在这种情况下,readyRead() SIGNAL 被发射 8 次。

void Client::SendMessage(std::vector<QString>)
{
    MyClass _Send;
            _Send.Age = 22;
            _Send.School = 14;
            _Send.Name = "Taz";

    QVector<MyClass2> vv;

    for (int i = 0; i < 15000; i++) {
        vv.push_back(MyClass2(24, "leri"));
        vv.push_back(MyClass2(22, "tazo"));
    }

    _Send.vctr = vv;

    QByteArray bytes;
    QDataStream stream(&bytes, QIODevice::WriteOnly);

    int FunctionUID = 331;
    int ij, ik = ij = 169;
    MyClass2 faf(-31, "15");

    stream << FunctionUID << _Send << faf << ij << ik;

    socket->write(bytes);
}



void Server::ImReady()
{
    QByteArray buf;

    buf = socket->readAll();

    QDataStream stream(&buf, QIODevice::ReadOnly);

    int FunctionUID, ij, ik;
    MyClass vv;
    MyClass2 vv1;

    stream >> FunctionUID >> vv >> vv1 >> ij >> ik;

    qDebug() << vv.vctr.size() << "faf";
}

void Server::incomingConnection(qintptr val)
{
    qDebug() << "Client Connected" << val;

    socket = new QTcpSocket;
    socket->setSocketDescriptor(val);

    if (!socket) {
        return;
    }

    connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
    connect(socket, SIGNAL(readyRead()), this, SLOT(ImReady()));
}

【问题讨论】:

  • 数据并不总是作为单个数据包发送。如果它足够大,它可以作为数据块发送。这可能就是您收到多个 readyRead 信号的原因。
  • 如果您使用的是 Qt 5.7 或更新版本,您可以利用 QDataStream 中的 read transactions。看看fortune client example 看看它是如何实现的。

标签: c++ qt qtcpsocket qbytearray qtcpserver


【解决方案1】:

TCP 不发送消息,它是一个数据流。这意味着您不能只读取那里的所有数据,然后考虑这条消息。取而代之的是,通常会发送一个包含消息大小的标头,然后接收者知道要读取多少才能获得整个消息(仅此一条,而不是下一条)。

这意味着你的广告位会做这样的事情

void Server::ImReady()
{
   uint32 size;
   socket->read(&size, 4);
   uint32 readTotal = 0;
   do {
     readTotal += socket->read(buffer, size-readTotal);
   } while (readTotal < size);
}

你可以写一行

if (socket->bytesAvailable() == 0)
  return;

在槽的开头,然后你根本不关心每条消息是否多次发出信号。

请注意,上面的代码需要额外的错误处理,例如您必须确保第一个 read 读取所有 4 个字节,并始终处理 -1 的返回值。

【讨论】:

  • 我认为,这很有帮助。但是我有 sum 问题,现在我是 'uint32' 并且没有匹配的调用 socket->read(QByteArray)
  • QTcpSocket::read 有重载,一个使用字节数组并返回读取的字节数,另一个返回 QByteArray。您必须根据使用的代码来调整代码。答案中的代码将使用字节数组,buffer 将是一个简单的char[]