我正在编写一个客户端-服务器应用程序的原型,在战斗模式下,应用程序必须每秒传输大约 60 次 QString、QVariant 形式的 ~ 200 个值。
问题是,以这种在客户端发送的速度,我成功地收到了大约 50 个数据包中的一个,在其他情况下,从流中获取的变量结果是无效的 ( QVariant::invalid
)
服务器代码:
void TDummyWorker::initialize()
{
timer = new QTimer(this);
timer->setInterval(10);
connect(timer,SIGNAL(timeout()),this,SLOT(foo())); //будем посылать данные с заданным интервалом
timer->start();
server = new QTcpServer(this);
connect(server,&QTcpServer::newConnection,this,&TDummyWorker::somebodyConnected);
server->listen(QHostAddress::Any, 33333);
}
void TDummyWorker::foo()
{
QVector <TNamedVariable> vars;
static int n=0;
int ndata = 200;
for (int i=0; i<ndata; i++)
{
//формируем фейковые данные
vars.append(TNamedVariable("variable_number_" % QString::number(i), (i%2 != 0 ? float((i+n)*0.01) : int(i+n)) ));
}
for (QMap<int ,QTcpSocket *>::iterator it = clients.begin(); it!=clients.end(); ++it)
{
sendToClient(*it, vars); //посылаем данные всем подключенным клиентам
}
n++;
if (n>1000000) n=0;
}
void TDummyWorker::sendToClient(QTcpSocket *socket, QVector <TNamedVariable> &vars)
{
QByteArray arr;
QDataStream out(&arr, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_2);
int n=vars.size();
out << quint16(0);
for(int i=0; i<n;i++)
{
out << vars[i];
}
out.device()->seek(0);
out << quint16(arr.size() - sizeof(quint16));
sendMessage(vars[0].value.toString() % " " % QString::number(arr.size() - sizeof(quint16)));
socket->write(arr);
}
这就是我在客户端获取数据的方式:
void TClient::recieveData()
{
QDataStream stream(socket);
stream.setVersion(QDataStream::Qt_5_2);
QVector <TNamedVariable> vars;
quint16 nextBlockSize=0;
while (true)
{
if (nextBlockSize==0)
{
if (socket->bytesAvailable() < sizeof(quint16))
{
break;
}
stream >> nextBlockSize;
}
if (socket->bytesAvailable() < nextBlockSize)
break;
TNamedVariable buf;
while (!stream.atEnd())
{
stream >> buf;
buf.debug();
vars.append(buf);
}
nextBlockSize = 0;
}
emit sendData(vars);
}
如果我们每秒发送 1-2 个变量,那么客户端会正常接受所有内容,我增加传输频率和向量中变量的数量越多,我得到的损坏数据就越多。我怀疑这个错误是微不足道的,因为代码几乎完全来自教科书示例。
所有编辑后正确工作的代码:
void TClient::recieveData()
{
QDataStream stream(socket);
stream.setVersion(QDataStream::Qt_5_2);
static int nbad = 0;
TNamedVariable buf;
static quint16 nextBlockSize=0;
while (true)
{
if (nextBlockSize == 0)
{
if (socket->bytesAvailable() < sizeof(quint16))
{
break;
}
stream >> nextBlockSize;
}
if (socket->bytesAvailable() < nextBlockSize)
{
break;
}
while (!stream.atEnd())
{
stream >> buf;
if (!buf.value.isValid())
{
qDebug()<<"op :("<<++nbad;
}
if (buf.name == "eod")
break;
vars.append(buf);
}
if (buf.name == "eod")
{
emit sendData(vars);
vars.clear();
}
nextBlockSize = 0;
}
}
问题最有可能出现在 中
nextBlockSize
,或者更确切地说,变量存在于本地,而不是在对 的调用之间保留其值recieveData()
。收到时,网络数据包可能包含 的值
nextBlockSize
,以及消息正文中的一些数据。然后检查的代码块nextBlockSize
将正常工作,但条件:... 将导致执行上下文退出该方法。然后
nextBlockSize
它将失去它的价值,并且recieveData()
使用新上传的数据的新呼叫将无法正常工作,因为。来自流的消息的大小先前已被读取。添加
另一个问题可能出现
while (!stream.atEnd()) {}
在recieveData()
. 接收到的消息,除了其内容之外,还可能包含下一条消息的部分数据。在这种情况下,指定的周期将无法正常工作。在所示示例中,只有一个循环
forever
,即在读取当前消息数据后从头开始重新迭代(包括检查消息大小)。receive_data_size
是消息大小。在示例中,它存储在套接字对象的属性中。如果多个套接字正在调用同一个接收槽,这将很有用。