【问题标题】:How does the process of sending data through a socket really work?通过套接字发送数据的过程是如何真正工作的?
【发布时间】:2015-12-03 13:09:48
【问题描述】:

我编写了一个服务器/客户端设置,可以来回发送字符串并且它可以工作。现在我正试图从一个不起作用的 php 脚本发送数据,所以我试图解开它究竟为什么不起作用。

这是从客户端发送数据的代码,我发送到服务器的字符串 = "aa"

(注意代码中的 cmets)

void Client::sendNewMessage(){
    qDebug() << "sendNewMessage()";

    QString string(messageLineEdit->text()); 

    QByteArray block;
    QDataStream out(&block, QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_4_0);
    out << quint16(0) << string; // why is the quint16 appended before the string?
    out.device()->seek(0); // set current position to 0, why exactly?
    out << (quint16)(block.size() - sizeof(quint16)); // substract 16bit unsigned int from the total data size? 
                                                      //Probably something to do with the appending of the quint16 at the beginning.

    tcpSocket->write(block);
}

这是服务器的读取功能:

void TcpServer::readIncomingData(){
    QDataStream in(tcpServerConnection);
    in.setVersion(QDataStream::Qt_4_0);

    int size = (int) sizeof(quint16); // get packetsize? size = 2 because quint16 is 2 bytes?
    qDebug() << "size = " << size;

    // ** OPTIONAL CODE, WORKS WITHOUT ASWELL ** // I got this somewhere from the internet.
    if (tcpServerConnection->bytesAvailable() < (int)sizeof(quint16))
        return; // if size of packet is less than 2, return. 
                // Because there is not enough bytes to correctly read the data?

    quint16 blockSize = 0;
    in >> blockSize; // i noticed that after this line executes
                     // tcpServerConnection->bytesAvailable is substracted by 2
                     // and blockSize = 8 instead of 10, because 
                     // tcpServerConnection->bytesAvailable starts with 10.
                     // it seems that the socket recognizes that a quint16 was appended
                     // before the actual data, hence the 8 bytes. Is this correct?

    if (tcpServerConnection->bytesAvailable() < blockSize)
        return;

    QString data;
    in >> data;
    qDebug() << "data = " << data;

所以这些问题的主要目的是能够将数据从 PHP 脚本发送到服务器,所以我需要(并且想要)知道整个过程是如何工作的。如果有人能照亮这个黑洞,我会很高兴:D

注意服务器和客户端是使用 QTcpSocket 和 QTcpServer 编写的。

【问题讨论】:

  • 套接字是顺序 I/O 设备,在它们上查找是无操作的。不要寻找他们!
  • ... 但是您的代码中的第一次搜索是有效的。您正在寻找缓冲区,而不是套接字:)
  • @KubaOber aaah 是的,我已经从下面的 cmets 中得到了:D 谢谢!!

标签: php c++ qt sockets stream


【解决方案1】:

TCP 是一个字节流 协议,这意味着数据以有序的字节流发送和接收,而不保留任何逻辑消息边界。在这方面,当您的终端设置为无缓冲模式时,读取数据有点像读取std::cin:您可能会得到用户输入的下一个字符,或 10 个字符,或一整行,一行半或下一个4k。您唯一可以确定的是,您不能得到比写入流更多的东西。当您有足够的数据进行有意义的处理时,由您自己决定:这可能是...

  • 扫描像'\n' 这样的标记字符,知道完整的输入行是不同的“逻辑”消息,值得处理

  • 为下一条逻辑消息添加长度,可以是固定长度字段(更简单)或可变长度文本,后跟已知分隔符,例如空格或换行符;这就是您的代码对 2 字节 quint16 size 值所做的事情

  • 将每个逻辑消息填充到固定长度

然后有必要保持read()ing 或recv()ing 直到读取了足够的字节供您处理下一条逻辑消息。

您的QDataStream 似乎可以通过read()ing/recv()ing 为您简化此操作 - 可能在后台线程中或当您的应用处于空闲状态时。它显然提供了bytesAvailable() 作为它已经从 TCP 流接收到的字节数并在其缓冲区中。

在客户端:

QString string(messageLineEdit->text()); 
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << quint16(0) << string; // why is the quint16 appended before the string?

这会写入一个 2 字节的“0”值,后跟来自 string 的文本。前者有效地为string 的长度保留空间。

out.device()->seek(0); // set current position to 0, why exactly?

这会在字符串之前跳回到上面写入 2 字节“0”值的位置...

out << (quint16)(block.size() - sizeof(quint16)); // substract 16bit unsigned int from the total data size? 

这会用字符串的实际大小覆盖上面写入的“0”值,它是通过从block 大小中减去 2 个字节来确定的。

在服务器端,似乎每次收到更多数据时都会调用该函数,并检查是否有足够的数据作为下一条消息进行解析。虽然它看起来有问题,好像有足够的数据来解析 size 但整个字符串还没有被缓冲,然后它返回并在这样做时丢弃了 blockSize 的所有知识,这已经被删除了来自QDataStream。相反,它应该记住 blockSize 某处(例如,在类成员变量中),并且下次调用该函数时,它应该从 if (tcpServerConnection-&gt;bytesAvailable() &lt; blockSize) 继续。

【讨论】:

  • @CantThinkOfAnything:正确的想法,虽然quint16 是两个字节,而不是位。 seek(0) 的事情很愚蠢,因为字符串的大小必须可以直接写入文件而没有这种恶作剧。
  • "这会在字符串之前跳回到上面写入 2 字节 "0" 值的位置..." 由于套接字是顺序的,因此对它们进行搜索没有任何意义并且是无操作的.这已记录在案:顺序设备不支持寻找任意位置。必须一次性读取数据。 pos()size() 函数不适用于顺序设备。 QTcpSocketQProcess 是顺序设备的示例。
  • 唉,当然,您不是将其写入套接字,而是写入缓冲区,所以我所说的关于 seek 的内容是正确的,但不适用于此处。 QBuffer 不是顺序设备。抱歉打扰了。
  • @CantThinkOfAnything 是的,关于 2 步而不是 3 步(或 4,如果你计算写入 0 长度,写入字符串,查找,写入正确长度)。不客气。不要忘记纠正上一段中提到的错误 - 当您的程序通过较慢/不太可靠的网络连接和/或更多负载/繁忙时传输大量数据时,最有可能导致失败的事情机器。干杯
  • @CantThinkOfAnything:是的,这就是它的工作方式......正确。干杯
猜你喜欢
  • 2010-12-07
  • 2016-04-06
  • 1970-01-01
  • 2011-06-09
  • 1970-01-01
  • 1970-01-01
  • 2010-11-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多