QTcpSocket::readAll() is empty

QTcpSocket::readAll() is empty

本文关键字:is empty readAll QTcpSocket      更新时间:2023-10-16

我是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(),但块为空,尽管在客户端调用tSock->write(block)块时不为空...问题似乎出在哪里?我将不胜感激任何帮助。谢谢!

编辑:您的错误是您在只读模式下打开out数据流,但尝试将收到的字节数组写入其中:

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

另外:请注意,Qt数据类型的序列化机制在这种情况下很有用:

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设备上读取/写入对象:QFile,QSerialPort,QProcess,QBuffer等。

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


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

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

算法的变体(Qt 4财富客户端示例):

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财富客户端示例

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

tcpsocket->waitForBytesWritten(1000);

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