Russian Qt Forum

Qt => Работа с сетью => Тема начата: vanchen от Июль 12, 2013, 16:28



Название: QSslSocket::write [РЕШЕНО]
Отправлено: vanchen от Июль 12, 2013, 16:28
Приветствую!

Использую Qt 4.7.4.
В потоке крутятся несколько сокетов. Они крайне активно отсылают сообщения в сеть.
Причем, сообщения не большие, но их много.
 Суть в том, что метод QSslSocket::write довольно быстро отъедает память. При закрытии сокета память чистится, но я считаю такое поведение странным. Сомневаюсь, что если я хочу отправить файл в 10гб мне необходимо несколько раз открывать и разрывать соединение для очистки памяти.
Отладка показала что Qt создает много объектов QMetaCallEvent, но далеко не все из них уничтожает. Что посоветуете?


Название: Re: QSslSocket::write
Отправлено: alexandros от Июль 12, 2013, 20:52
код в студию


Название: Re: QSslSocket::write
Отправлено: vanchen от Июль 15, 2013, 09:41
Сервер:
Код:
void MemoryLeakTest::pbStartClicked()
{
TCPServer * serv = new TCPServer();
connect(serv, SIGNAL(newConnection(qint32)), this, SLOT(newClient(qint32)));
if (serv->listen(QHostAddress::Any, 2000))
qDebug()<<"listening";
}

void MemoryLeakTest::newClient(qint32 socketDescriptor)
{
client = new QSslSocket();
connect(client, SIGNAL(readyRead()), this, SLOT(canRead()));
connect(client, SIGNAL(sslErrors(QList<QSslError>)), client, SLOT(ignoreSslErrors()));
client->setSocketDescriptor(socketDescriptor);
client->setPrivateKey("C://defaultKeySpiral.key");
client->setLocalCertificate("C://defaultCertSpiral.crt");
client->startServerEncryption();
qDebug()<<"encrypted "<<(client->waitForEncrypted(3000) ? "true" : client->errorString());
}

void MemoryLeakTest::canRead()
{
client->readAll();
}




Клиент:
Код:
void ThreadClient::run()
{

QSslSocket * client = new QSslSocket();
connect(client, SIGNAL(sslErrors(QList<QSslError>)), client, SLOT(ignoreSslErrors()));
client->connectToHost("127.0.0.1", 2000);
qDebug()<<"Connected "<<client->waitForConnected(3000);
client->setPrivateKey("C://defaultKeyAgent.key");
client->setLocalCertificate("C://defaultCertAgent.crt");
client->startClientEncryption();
if (client->waitForEncrypted(3000))
{
QString mes;
mes.append("sdsds");
for (int i=0;i<500000;++i)
{

client->write(mes.toAscii());
client->waitForBytesWritten(3000);
}
}
else
qDebug()<<client->errorString();

qDebug()<<"finished";
}

Вот тестовый код. Проблема проявляется при установлении защищенного соединения. При отправке сообщений на клиенте растет память. Причем, чем больше сообщений тем больше памяти съедается. Т.е. на размер отъедаемой памяти влияет не размер сообщения а количество отправляемых сообщений.


Название: Re: QSslSocket::write
Отправлено: alexandros от Июль 15, 2013, 20:53
попробуй очищать

socket->close();
socket->disconnectFromHost();
socket->delteLater()


Название: Re: QSslSocket::write
Отправлено: vanchen от Июль 16, 2013, 09:41
Если закрывать то все прекрасно очищается. Тут вопросов нету. Вопрос в том: почему растет память во время работы. Если я хочу отправлять очень много сообщений не разрывая соединения то память будет расти.


Название: Re: QSslSocket::write
Отправлено: vanchen от Июль 16, 2013, 16:09
Разобрался....

Судя по исходникам Qt:
Код:
qint64 QSslSocket::writeData(const char *data, qint64 len)
{
    Q_D(QSslSocket);
#ifdef QSSLSOCKET_DEBUG
    qDebug() << "QSslSocket::writeData(" << (void *)data << ',' << len << ')';
#endif
    if (d->mode == UnencryptedMode && !d->autoStartHandshake)
        return d->plainSocket->write(data, len);

    char *writePtr = d->writeBuffer.reserve(len);
    ::memcpy(writePtr, data, len);

    // make sure we flush to the plain socket's buffer
QMetaObject::invokeMethod(this, "_q_flushWriteBuffer", Qt::QueuedConnection);

    return len;
}

Когда соединение не шифрованное данные уходят сразу на сокет.
Код:
if (d->mode == UnencryptedMode && !d->autoStartHandshake)
        return d->plainSocket->write(data, len);

Но когда соединение шифрованное, вызывается QMetaObject::invokeMethod с параметром  Qt::QueuedConnection.
Событие помещается в очередь и будет обработано только если запущен EventLoop.

Код:
void ThreadClient::run()
{

QSslSocket * client = new QSslSocket();
connect(client, SIGNAL(sslErrors(QList<QSslError>)), client, SLOT(ignoreSslErrors()));
connect(client, SIGNAL(encryptedBytesWritten(qint64)), this, SLOT(bytesWritten(qint64)));
client->connectToHost("127.0.0.1", 2000);
emit toLog(QString("Connected ") + (client->waitForConnected(3000) ? "true" : "false"));
client->setPrivateKey("C://defaultKeyAgent.key");
client->setLocalCertificate("C://defaultCertAgent.crt");
client->startClientEncryption();
if (client->waitForEncrypted(30000))
{
QString mes;
mes.append("sdsds");
for (int i=0;i<500000;++i)
{

client->write(mes.toAscii());
QCoreApplication::processEvents();
client->waitForBytesWritten(3000);

}
}
else
emit toLog(client->errorString());

emit toLog("finished");
}

Таким образом, добавление в приведенном примере QCoreApplication::processEvents() решает проблему.