使用QT通过TCP插座在零件中发送XML消息
-
28-09-2019 - |
题
我们正在编写一个项目,其中有一个客户生成XML请求的客户,将其发送到服务器,后者解析请求并将请求信息返回XML字符串中。
当XML答复很小时,该应用程序正常工作,但是当它们超过2500个字符时,有时会在客户端端被切断。我有时会说,因为当客户端和服务器在同一台计算机上运行并通过家庭地址进行通信127.0.0.1交流时,答复就可以解析了。但是,当客户端和服务器在不同的机器上并通过LAN进行通信时,这是客户将消息剪切到大约2500个字符的时候。
通信由TCP插座完成。我们正在使用QT,客户端具有QTCPSOCTED,服务器是QTCPSERVER和指向QTCPSOCTED的指针。
我们认为我们问题的可能解决方案是将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);
当它获得新连接时,它将调用看起来像这样的接受插槽
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();
}
}}
在我看来,启动读取的编码方式是在客户端写消息时,服务器都会读取并将其附加到服务器存储的XMLRequest。如果包含消息,则XML关闭标签,则处理请求。
不过发生的事情是,如果客户端确实写了连续写作,则服务器并不全部读取它们,只有第一个,并且永远不会收到XML关闭标签,因此没有请求。
我需要回答的问题是,为什么服务器不响应客户端的多个写入?我应该如何制作它,以使我可以发送XML字符串,分解碎片,并使服务器读取所有件并再次将其变成一个字符串?
解决方案
这是由于TCP协议的“流”性质而发生的。数据分为许多数据包,在您的应用中,您实际上只读取其中的一部分(Bytesavailable()不需要等于其他主机发送的字节数量,这是在该字节中,正是多少个字节可用插座缓冲区)。您要做的是在客户端和服务器之间建立一个简单的协议。例如,客户端首先发送STX字符,然后发送XML,然后发送ETX字符。当服务器看到STX字符时,它会将所有内容读取到缓冲区中,直到EXT字符为止。其他方法 - 在网络字节顺序中发送一个4字节整数,指示字节中XML数据的大小,然后发送XML。另一个主机必须接收整数,将其转换为本机字节订单,然后读取从套接字到缓冲区的指定数量数据。
其他提示
在TCP中,有一种称为最大段的大小。在数据传输初始化之前,双方在SYN握手阶段决定MSS。这就是为什么您的数据被拆分的原因。
您只有一个client.read()。服务器将为每个已处理的读取。您需要在客户端的类似机制来处理读取。读取直到读取n个字节数的函数。您可以在数据传输开始时发送值n。
Comp 3004我看到了。这样的噩梦,我们一直在尝试使用QXMLStreamReader和QXMLStreamWriter尝试。作者很简单,但是读者是一场噩梦,我们一直在尝试使用过早的文件错误作为知道有更多数据的突破点。