1

我是 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)调用时块不为空......似乎是哪里的问题?我将不胜感激任何帮助。谢谢!

4

3 回答 3

2

编辑:您的错误是您以只读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 设备的对象: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

于 2017-06-13T19:08:38.253 回答
0

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;
  }
};
于 2017-06-14T14:06:52.717 回答
0

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

tcpsocket->waitForBytesWritten(1000);
于 2017-06-14T13:38:40.747 回答