使用 Qt 通过 TCP 套接字分部分发送 xml 消息

发布于 2024-10-04 17:35:29 字数 3545 浏览 0 评论 0原文

我们正在编写一个项目,其中有一个客户端生成 xml 请求,将它们发送到服务器,服务器解析该请求并以 xml 字符串返回所请求的信息。

当 xml 回复很小时,应用程序工作正常,但当它们超过大约 2500 个字符时,它们有时会在客户端被截断。我说有时是因为当客户端和服务器在同一台机器上运行并通过主地址 127.0.0.1 进行通信时,回复会被很好地解析。但是,当客户端和服务器位于不同的计算机上并通过 LAN 进行通信时,客户端会将消息剪切为大约 2500 个字符。

通信是通过 tcp 套接字完成的。我们使用Qt,客户端有一个qTCPsocket,服务器有一个qTCPserver和一个指向qtcpsocket的指针。

我们认为解决这个问题的一个可能的解决方案是分段发送 xml,按字符数或标记分隔。虽然我们很容易将消息分成几部分,但发送这些部分并让客户端或服务器读取这些部分并将其编译为一个 xml 请求会给我们带来麻烦。

举例来说,我们想要测试让客户端分多个部分发送请求。

这是我们发送请求的客户端函数调用。 xmlReq 在其他位置生成并传入。作为将消息分成几部分的示例,我们从 xml 请求中删除结束标记,然后将其作为另一部分发送。

QString ClientConnection::sendRequest(QString xmlReq)
{

    this->xmlRequest = xmlReq;

    QHostAddress addr(address);

    QList<QString> messagePieces;
    xmlRequest.remove("</message>");

    messagePieces.append(xmlRequest);
    messagePieces.append("</message>");


    client.connectToHost(addr,6789);

    if(client.waitForConnected(30000))
    {
        for(int i = 0; i < messagePieces.length();i++)
        {  
            client.write(messagePieces[i].toAscii(),messagePieces[i].length()+1);
            qDebug() << "Wrote: " << messagePieces[i];
        }
    }


    char message[30000] = {0};

    xmlReply = "";

    if(client.waitForReadyRead(30000)){


        client.read(message,client.bytesAvailable());


    }else{
        xmlReply = "Server Timeout";
    }

    client.close();
    xmlReply = (QString) message;

    return xmlReply;

}

接下来是我们的服务器代码。它的编写方式是从客户端读取消息,直到看到 xml 结束消息标记,然后处理数据并将回复发送回客户端。

这是启动服务器的代码。

//Start the server, pass it the handler so it can perform queries
    connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
    server.listen(QHostAddress::Any, 6789);

当它获得一个新连接时,它会调用acceptConnection槽,它看起来像这样

    void CETServer::acceptConnection()
{
    client = server.nextPendingConnection();
    connect(client, SIGNAL(readyRead()), this, SLOT(startRead()));
}

startRead看起来像这样:

void CETServer::startRead()
{
    char buffer[1024*30] = {0};

    client->read(buffer, client->bytesAvailable());

    QString readIn;

    readIn = (QString) buffer;

    ui->statusText->appendPlainText("Received: " + readIn);

    //New messages in will be opened with the xml version tag
    //if we receive said tag we need to clear our query
    if (readIn.contains("<?xml version =\"1.0\"?>",Qt::CaseSensitive))
    {
        xmlQuery = "";
    }

    //add the line received to the query string
    xmlQuery += readIn;

    //if we have the clsoe message tag in our query it is tiem to do stuf with the query
    if(xmlQuery.contains("</message>"))
    {
        //do stuff with query

        ui->statusText->appendPlainText("Query received:" + xmlQuery);

        QString reply = this->sqLite->queryDatabase(xmlQuery);
        xmlQuery = "";

        this->commandStatus(reply);

        if(client->isWritable()){
            //write to client
            client->write(reply.toAscii(),reply.length()+1);
            ui->statusText->appendPlainText("Sent to client: " + reply);
            client->close();

        }
    }}

在我看来,start read的编码方式是这样的:每当客户端写入消息时,服务器都会读取它并将其附加到服务器存储的 xmlRequest。如果消息包含 xml 结束标记,则它会处理该请求。

但是,如果客户端连续进行写入,服务器不会读取所有写入,而只会读取第一个写入,并且永远不会收到 xml 结束标记,因此不会处理任何请求。

我需要回答的问题是为什么服务器不响应客户端的多次写入?我应该如何制作它,以便我可以发送一个 xml 字符串,分成几部分,并让服务器读取所有部分并将其再次转换为一个字符串?

We are writing a project where there is a client who generates xml requests, sends them to a server, who parses the request and returns the requested information in an xml string.

The application works fine when the xml replies are small, but when they exceed about 2500 characters they sometimes get cut off on the client end. I say sometimes because when the client and server run on the same machine and communicate through the home address 127.0.0.1 the replies are parsed just fine. However, when the client and server are on different machines and communicate over lan, this is when the client cuts the message to around 2500 characters.

The communication is done by tcp sockets. We are using Qt, the client has a qTCPsocket, and the server a qTCPserver and a pointer to a qtcpsocket.

We think a possible solution to our issue is sending the xml in pieces, either separated by character count or by tag. While it is easy for us to break the message into parts, sending the parts and having the client or server read and compile the parts into one xml request is causing us trouble.

For the sake of example, we wanted to test having the client send a request in multiple parts.

Here is our client function call to send a request. xmlReq is generated else where and passed in. As an example of breaking the message into parts we strip away the closing tag from the xml request, and then send it as another piece later.

QString ClientConnection::sendRequest(QString xmlReq)
{

    this->xmlRequest = xmlReq;

    QHostAddress addr(address);

    QList<QString> messagePieces;
    xmlRequest.remove("</message>");

    messagePieces.append(xmlRequest);
    messagePieces.append("</message>");


    client.connectToHost(addr,6789);

    if(client.waitForConnected(30000))
    {
        for(int i = 0; i < messagePieces.length();i++)
        {  
            client.write(messagePieces[i].toAscii(),messagePieces[i].length()+1);
            qDebug() << "Wrote: " << messagePieces[i];
        }
    }


    char message[30000] = {0};

    xmlReply = "";

    if(client.waitForReadyRead(30000)){


        client.read(message,client.bytesAvailable());


    }else{
        xmlReply = "Server Timeout";
    }

    client.close();
    xmlReply = (QString) message;

    return xmlReply;

}

Next is our server code. It is written in such away that it is supposed to read messages from the client until it sees the xml closing message tag, and then process the data and send the reply back to the client.

This is the code that starts the server.

//Start the server, pass it the handler so it can perform queries
    connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
    server.listen(QHostAddress::Any, 6789);

When it gets a new connection it calls the acceptConnection slot which looks like this

    void CETServer::acceptConnection()
{
    client = server.nextPendingConnection();
    connect(client, SIGNAL(readyRead()), this, SLOT(startRead()));
}

The startRead looks like this:

void CETServer::startRead()
{
    char buffer[1024*30] = {0};

    client->read(buffer, client->bytesAvailable());

    QString readIn;

    readIn = (QString) buffer;

    ui->statusText->appendPlainText("Received: " + readIn);

    //New messages in will be opened with the xml version tag
    //if we receive said tag we need to clear our query
    if (readIn.contains("<?xml version =\"1.0\"?>",Qt::CaseSensitive))
    {
        xmlQuery = "";
    }

    //add the line received to the query string
    xmlQuery += readIn;

    //if we have the clsoe message tag in our query it is tiem to do stuf with the query
    if(xmlQuery.contains("</message>"))
    {
        //do stuff with query

        ui->statusText->appendPlainText("Query received:" + xmlQuery);

        QString reply = this->sqLite->queryDatabase(xmlQuery);
        xmlQuery = "";

        this->commandStatus(reply);

        if(client->isWritable()){
            //write to client
            client->write(reply.toAscii(),reply.length()+1);
            ui->statusText->appendPlainText("Sent to client: " + reply);
            client->close();

        }
    }}

In my mind, the start read is coded in such a way that anytime the client writes a message, the server reads it and attaches it to the xmlRequest the server stores. If the message contains , the xml closing tag, then it processes the request.

What happens though, is if the client does successive writes, the server does not read them all, only the first, and never receives the xml closing tag, and therefore processes no requests.

The question I need answered is why doesn't the server respond to the clients multiple writes? How should I go about making it such that I can send an xml string, broken down in pieces, and have the server read all the pieces and turn it into one string again?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

花开半夏魅人心 2024-10-11 17:35:29

在 TCP 中,有一个称为最大段大小的东西。在数据传输初始化之前,双方在SYN握手阶段决定MSS。这就是您的数据被分割的原因。

你只有一个 client.read() 。服务器将为每次处理的读取发送回复。您需要在客户端使用类似的机制来处理读取。一个读取直到读取 N 个字节的函数。您可以在数据传输开始时发送值 N。

In TCP, there is something known as the Maximum Segment Size. Before initialization of data transfer, both parties decide the MSS in the SYN handshake phase. That is the reason why your data is being split up.

You have only one client.read() . The server will send a reply for every read processed. You need a similar mechanism on the client side to handle reads. A function that reads till it has read N number of bytes. You can send the value N at the start of your data transfer.

你如我软肋 2024-10-11 17:35:29

这是由于 TCP 协议的“流”性质而发生的。数据被分成许多数据包,在您的应用程序中,您实际上只读取其中的一部分(bytesAvailable() 不一定等于其他主机发送的字节数,它只是数据包中有多少可用字节。套接字缓冲区)。
你所要做的就是在客户端和服务器之间建立一个简单的协议。例如,客户端首先发送 STX 字符,然后发送 XML,最后发送 ETX 字符。
当服务器看到 STX 字符时,它会将所有内容读入缓冲区,直到 EXT 字符。
其他方法 - 以网络字节顺序发送 4 字节整数,指示 XML 数据的大小(以字节为单位),然后发送 XML。另一台主机必须接收该整数,将其转换为其本机字节顺序,然后将指定数量的数据从套接字读取到缓冲区。

That is happening because of the "stream" nature of the TCP protocol. The data is split into many packets, and in your app, you're really reading only a part of them (bytesAvailable() is not necessary equal to the amount of bytes the other host sent, it's just how many bytes are available in the socket buffer).
What you have to do is to establish a simple protocol between the client and server. For example, the client first sends the STX character, then the XML, then ETX character.
When the server sees an STX character, it reads everything into the buffer up until the EXT character.
Other approach - send a 4-byte integer in network byte order indicating the size of XML data in bytes, then send XML. The other host must receive the integer, convert it to its native byteorder, then read the specified amount of data from the socket to the buffer.

迟月 2024-10-11 17:35:29

COMP 3004 我明白了。这样的噩梦,我们一直在尝试使用 QXmlStreamReader 和 QXmlStreamWriter。作者很好,很简单,但读者却是一场噩梦,我们一直在尝试使用 PrematureEndOfDocument 错误作为断点,以了解还有更多数据。

COMP 3004 I see. Such a nightmare, we've been trying with QXmlStreamReader and QXmlStreamWriter. The writer is nice and simple, but the reader is a nightmare, we've been trying to use the PrematureEndOfDocument error as a break point for knowing there is more data.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文