PDA

View Full Version : Problem with IMAP and QTcpSocket



#Dragon
16th July 2016, 15:24
Hello, I am writing an Imap client but I need Your help to improve my code, I have tried to solve it, but I don't know how. I guess it is some problem with too big data stored in QByteArray

As first my program generates very hight CPU usage when I try download using Imap, it is 100% of my Core i7. I am sure it is some bug in download function because as normal does not exceed 10%
Second problem I found is that download speed slows down about 10kb/s and finish on few bytes/s making my client unusable, (can't download bigger file than 3mb)


Imap.cpp (download function)


void Imap::downloadFile(int message_id, QString filename){
m_logger->Debug("IMAP - downloading: " + this->m_login);
this->login();
sendCommand("a03 EXAMINE INBOX", "a03");
m_dowloadFile = true;
qint64 size = 0;
for(int i = 0; i < m_mailData.count(); i++){
if(m_mailData[i].messageId == message_id){
size = m_mailData[i].size;
}
}
this->getProgressListener()->startTimer();
this->getProgressListener()->setTotal(size);
m_file = new QFile(filename);
if(m_file->open(QIODevice::WriteOnly)) {
sendCommand("a09" + QString::number(message_id) + " fetch " + QString::number(message_id) + " BODY.PEEK[2]", "a09" + QString::number(message_id));
m_file->close();
} else {
m_statusCode = Mailbox::FileWriteError;
}
this->disconectFromHost();
delete m_file;
m_file = NULL;
m_dowloadFile = false;
}


Part of SocketsBase.cpp


ocketsBase::SocketsBase(QString server, int port, bool ssl_mode, EConnectionType type, QObject *parent) :
QObject(parent)
{
m_server = server;
m_port = port;
m_SSLMode = ssl_mode;
m_logger = Logger::getInstance();
m_connectionType = type;
m_file = NULL;
m_dowloadFile = false;
m_finished = false;
m_running = false;
m_timer = new QTimer;
m_Sockets = (m_SSLMode) ? new QSslSocket(this) : new QTcpSocket(this);
connect(m_Sockets, &QAbstractSocket::readChannelFinished, [=]()
{
emit finished();
});

connect(m_Sockets, SIGNAL(readyRead()), this, SLOT(ready_read()), Qt::DirectConnection);
connect(m_Sockets, SIGNAL(connected()), this, SLOT(connected()));
connect(m_Sockets, SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(errorReceived(QAbstractSocket::SocketErr or)));
connect(m_Sockets, SIGNAL(stateChanged(QAbstractSocket::SocketState)) , this, SLOT(stateChanged(QAbstractSocket::SocketState)));
m_success = true;
}

void SocketsBase::ready_read()
{
m_finished = false;
connect(m_timer, &QTimer::timeout, [=]() {
emit timeOutReached();
});
m_timer->start(240000);


if(m_dowloadFile)
{
ProgressListener::getInstance(m_login)->updateProgress(ProgressListener::getInstance(m_log in)->getCurrent() + m_Sockets->bytesAvailable());
m_logger->Debug(trUtf8("Bytes available %s"), qPrintable(QString::number(m_Sockets->bytesAvailable())));
}

while(!m_Sockets->atEnd()){
if(m_Sockets->bytesAvailable() > 4096){
m_dataBuffer.append(m_Sockets->read(4096));
}else{
m_dataBuffer.append(m_Sockets->readAll());
}
}
m_Sockets->flush();
m_logger->Error(QString::number(m_dataBuffer.size()));


QRegExp re1(m_currentCode);
QRegExp re(".[\r][?\n]?a[0-9]+ OK Success.*");
QRegExp re2(".[\r]?[\n]?a[0-9]+ OK [fetchFETCH]+.*");

if(((re1.indexIn(m_dataBuffer) != -1 && !m_dowloadFile) || (m_dowloadFile && re.indexIn(m_dataBuffer) != -1) )|| re2.indexIn(m_dataBuffer) != -1 || m_currentCode == "-1" || m_connectionType == POP3)
{
if(m_dowloadFile)
{
if(m_file)
{
if(!m_file->isOpen())
if(!m_file->open(QIODevice::WriteOnly))
{
m_logger->Error(trUtf8("Couldn't open file %s to write"), qPrintable(m_file->fileName()));
}

QTextStream out(m_file);
out << m_dataBuffer;
//...
}
}
m_timer->stop();
emit finished();
}
//
//...
//...
//
else if(m_dataBuffer.contains("NULL") || m_dataBuffer.contains("null")){
m_logger->Error("Buffer is null !");
m_dataBuffer.clear();
emit finished();
}
}

m_dataBuffer is QByteArray

Any suggestions ?

#Dragon
18th July 2016, 22:02
This is very important for me, please give me some suggestions. Do You need more code ? Better description or additional informations ?

Cheers

anda_skoa
19th July 2016, 00:30
Some observations:

1) you send the fetch command and immediately after close the file
2) you then disconnect and delete the file

so how does that even get any data?
Smells like nested event loop but cold even be as worse as busy-looping

3) in your ready_read slot you locally create the regular expressions every time?
4) you parse the buffer every time?
5) if m_dataBuffer is a QByteArray, why write it with a QTextStream?

6) you use a lambda to emit finished() when the socket emits readChannelFinished, that cane done way simpler with a signal/signal connection

Cheers,
_

#Dragon
1st August 2016, 19:18
Thank You ! :) It works really good after Your suggestions