【问题标题】:How do I read a string sent from PHP through a socket to a Qt server application?如何读取从 PHP 通过套接字发送到 Qt 服务器应用程序的字符串?
【发布时间】:2015-12-04 14:38:57
【问题描述】:

我很难读取通过套接字连接发送到 Qt 服务器应用程序的字符输入。数据由 PHP 发送。

我了解读取流数据的原理,因为我已经在堆栈上问过这个问题。我还使用 Qt 编写的服务器和客户端使其工作。

我使用的方法是将要发送的数据的字节大小附加在实际数据之前。然后当数据进来时,我首先读取长度部分,这样我就知道我必须读取多少字节才能正确形成数据。

看起来像这样:

发送函数:

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);
    out  << string;
    out.device()->seek(0);
    out << (quint16)(block.size() - sizeof(quint16));

    tcpSocket->write(block);
}

接收函数:

QDataStream in(tcpServerConnection);
in.setVersion(QDataStream::Qt_4_0);
qDebug() << "bytes available = " << tcpServerConnection->bytesAvailable();

if (blockSize == 0) {
    int size = (int) sizeof(quint16);
    qDebug() << "size = " << size;
    if (tcpServerConnection->bytesAvailable() < (int)sizeof(quint16)){
        qDebug() << "less bytes than size...";
        return;
    }
    qDebug() << "bytes available=" << tcpServerConnection->bytesAvailable();
    in >> blockSize;
}

if (tcpServerConnection->bytesAvailable() < blockSize){
    qDebug() << "less bytes available than blocksize, bytes="
                   << tcpServerConnection->bytesAvailable();
    return;
}

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

好的,这一切都有效,所以我尝试用 PHP 做,但失败了

这是我的尝试之一:

<?php

$addr = gethostbyname("127.0.0.1");

$client = stream_socket_client("tcp://$addr:*****", $errno, $errorMessage);

if ($client === false) {
    throw new UnexpectedValueException("Failed to connect: $errorMessage");
}

$data = 'a';
$datatopost = serialize($data);

fwrite($client, strlen($data));
fwrite($client, base64_encode($data));
echo stream_get_contents($client);
fclose($client);

在 Qt 中,我尝试了 quint8、16、32、64、sizeof(char)、sizeof(int) 的各种组合。

在 PHP 中,我尝试过对数据进行序列化、编码,并在没有这些东西的情况下发送它。但我无法让它工作。我必须非常接近,因为数据实际上是发送的,因为有可用的字节,但我不知道如何正确编码/解码以使其工作。

在问了关于这个话题的各种问题后,我确实觉得我的理解有了很大的提高,但我仍然缺少关于如何实际做事的重要信息。

所以我的问题是:这里出了什么问题,需要采取哪些步骤才能将数据从 PHP 读取到 Qt/C++?

非常感谢细节,因为我真的很想知道事情是如何从内到外的。

side-note 从 PHP 脚本发送数据后,服务器将数据发送回原样,这样就可以了。这样连接就成功了

更新 这是实际也收到回复的工作 php 脚本:

<?php

if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0)))
{
    perror("Could not create socket");
}
echo "Socket created n";

//Connect socket to remote server
if(!socket_connect($sock , '127.0.0.1' , *****))
{
    perror("Could not connect");
}
echo "Connection established n";

$message = "aa";
//Send the message to the server
if( ! socket_send ( $sock , $message , strlen($message) , 0))
{
    perror("Could not send data");
}
echo "Message send successfully n";

//Now receive reply from server
if(socket_recv ( $sock , $buf , 500 , MSG_WAITALL ) === FALSE)
{
    perror("Could not receive data");
}
echo $buf;

///Function to print socket error message
function perror($msg)
{
    $errorcode = socket_last_error();
    $errormsg = socket_strerror($errorcode);

    die("$msg: [$errorcode] $errormsg n");
}

从浏览器 url 执行时的脚本回复:

Socket created nConnection established nMessage send successfully n hello

【问题讨论】:

  • QDataStream 需要对数据进行特殊编码。您确定您的 PHP 代码正在以 QDataStream 4.0 格式读取/写入数据吗?否则两端都会看到垃圾......
  • @tux3 好吧,我肯定没有在 PHP 中指定任何 QDataStream 格式。尽管 php 脚本确实回显了服务器发回的数据。我在上面发布的代码再次被编辑了一点,所以现在 php 脚本没有收到任何数据。但我相信一个小的编辑会解决这个问题。服务器确实收到了字节,我不知道这是否足够。我有点迷失在我尝试过的所有各种事情中,我忘记记录 100% 有效的事情
  • 没关系。我错了。我不确定您是否应该使用QDataStream 阅读。相反 - 创建您自己的协议,例如 - 前 8 个字节 = 数据包大小,然后发送原始数据。并直接读入,使用QIODevice::read 方法(在QTcpSocket 中重新实现)。
  • 因为QDataStream 实现了它自己的序列化协议。如果您想使用QDataStream,请查看readRawDatawriteRawData 方法。
  • 我不懂PHP,所以帮不上忙。请注意类型大小和字节顺序(大/小端)的错误。

标签: php c++ qt sockets


【解决方案1】:

PHP 代码没有集成并不奇怪。如前所述,您必须知道 QDataStream 实现了自定义序列化。并且正如还提到的,如果您阅读的内容通常以前没有使用 QDataStream 序列化,您可能想要使用(read|write)RawData(read|write)Bytes。但是,您尝试从 PHP 写入字符串数据的方式的一般想法应该与 Qt 编码字符串的方式兼容(长度然后是一系列字符。无论如何,这就是手册所说的......) .但也有一些问题。

  • QString 是 2Byte Unicode。
  • PHP 字符串是任意类型的 ASCII 兼容数据的字节数组 - PHP String details
  • 这个位有几个问题:

    fwrite($client, strlen($data));
    fwrite($client, base64_encode($data));
    

    strlen() 返回底层存储中的字节数(这是 ASCII 字符串的实际字节长度)。 base64_encode() 更改字符串中的字节数。并且您假设 fwrite() 正在写入一个四字节整数。它的类型转换和写入字符串。

  • 我们还在猜测如何

    QString data; 
    in >> data;
    

    确实有效。

一般建议是,您必须仔细定义外部二进制 API。

【讨论】:

  • 别管我说什么,我没有意识到 readRawData 只是读取字节而没有任何编码。我假设这与 QTcpSocket::readAll 相同,它也读取原始数据
  • 是的,没错。 @hank 的解决方案有效(并且更优雅),因为它没有对数据格式做出错误的假设。它只是读/写原始字节。如果您想对收到的数据进行处理而不是将其打印到屏幕或将其回显给发送者,那么您需要开始担心让每个人都同意数据的编码方式等。
【解决方案2】:

您是否需要为此任务进行数据序列化?您的 PHP 客户端和 Qt 服务器可能使用不同的格式。 尝试发送和接收原始数据。

这是一个简单的QTcpServer 示例:

class DataReceiver : public QObject
{
    Q_OBJECT
public:
    explicit DataReceiver(QObject *parent = 0);

public slots:
    void start(quint16 port = 9090);

private slots:
    void newTcpConnection();

private:
    QTcpServer server;
};

DataReceiver::DataReceiver(QObject *parent) :
    QObject(parent)
{
    connect(&server, SIGNAL(newConnection()), this, SLOT(newTcpConnection()));
}

void DataReceiver::start(quint16 port)
{
    bool isOk = server.listen(QHostAddress::Any, port);

    if (isOk && server.isListening())
    {
        qDebug() << "QTcpServer started on port" << port;
    }
    else
    {
        qCritical() << "Failed to start QTcpServer";
    }
}

void DataReceiver::newTcpConnection()
{
    qDebug() << "New incoming connection";

    QTcpSocket *socket = server.nextPendingConnection();

    QByteArray data;

    while (true)
    {
        QByteArray tmp = socket->readAll();
        data += tmp;

        if (tmp.isEmpty() && !socket->waitForReadyRead())
        {
            break;
        }
    }

    socket->deleteLater();

    qDebug("Data received: %s (len = %d)", data.constData(), data.length());
}

启动服务器:

#include <QCoreApplication>

#include "data_receiver.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    DataReceiver d;
    d.start();

    return a.exec();
}

您可以使用 PHP 客户端向其发送数据:

<?php

$addr = gethostbyname("127.0.0.1");
$port = 9090;
$data = 'hello from php';

$client = stream_socket_client("tcp://$addr:$port", $errno, $errorMessage);

if ($client === false) {
    throw new UnexpectedValueException("Failed to connect: $errorMessage");
}

fwrite($client, $data);
fclose($client);

或者您可以使用nc 实用程序:

echo -n "hello from nc" | nc 127.0.0.1 9090

这是两种情况的服务器输出:

QTcpServer started on port 9090 
New incoming connection 
Data received: hello from php (len = 14)
New incoming connection 
Data received: hello from nc (len = 13)

【讨论】:

  • 您能解释一下为什么即使人们告诉我应该使用 readRawData 还是可以这样做吗?
  • @CantThinkOfAnything 当你可以直接从QTcpSocket 对象读取数据时,我不知道为什么你需要使用QDataStream
  • 我也不是,哈哈。我仍然是编程的新手,我看到了所有使用 QDataStream 的示例。由于它是一个正在发送的字节流,我认为 QDataStream 将是用于这类东西的常见事物......显然我错了。
  • 首先QDataStream 应该用于序列化/反序列化任务。 The docs 明确表示:QDataStream 类提供二进制数据到 QIODevice 的序列化。如果您只需要从设备读取一些原始数据,QIODevice::readAll() 完全可以。
  • 这很有意义,感谢您澄清这一点
猜你喜欢
  • 2020-01-28
  • 1970-01-01
  • 2021-04-29
  • 1970-01-01
  • 2016-09-15
  • 1970-01-01
  • 1970-01-01
  • 2020-11-01
  • 1970-01-01
相关资源
最近更新 更多