-
Notifications
You must be signed in to change notification settings - Fork 161
Description
用定时器不停地访问服务器,会造成系统崩溃。经检查,是多线程引起的,可能会存在还在处理时,连接被关闭的情况。
修改如下:
class JQLIBRARY_EXPORT Session: public QObject 类增加一个参数:
QFuture m_workingFuc;
void JQHttpServer::AbstractManage::handleAccepted(const QPointer< Session > &session)
{
session->m_workingFuc = QtConcurrent::run( handleThreadPool_.data(), this, session {
//emit onRedReady(session);
if ( !this->httpAcceptedCallback_ )
{
qDebug() << "JQHttpServer::Manage::handleAccepted: error, httpAcceptedCallback_ is nullptr";
return;
}
this->httpAcceptedCallback_( session , this->getMainObject());
} );
}
然后在Session所有删除自身前,等待线程结束:
#define JQHTTPSERVER_SESSION_REPLY_PROTECTION2( functionName, ... )
if ( ioDevice_.isNull() )
{
qDebug().noquote() << QStringLiteral( "JQHttpServer::Session::" ) + functionName + ": error1";
m_workingFuc.waitForFinished();
this->deleteLater();
return VA_ARGS;
}
void JQHttpServer::Session::replyFile(const QString &filePath, const int &httpStatusCode)
{
JQHTTPSERVER_SESSION_REPLY_PROTECTION( "replyFile" )
if ( QThread::currentThread() != this->thread() )
{
replyHttpCode_ = httpStatusCode;
QMetaObject::invokeMethod( this, "replyFile", Qt::QueuedConnection, Q_ARG( QString, filePath ), Q_ARG( int, httpStatusCode ) );
return;
}
JQHTTPSERVER_SESSION_REPLY_PROTECTION2( "replyFile" )
replyIoDevice_.reset( new QFile( filePath ) );
QPointer< QFile > file = ( qobject_cast< QFile * >( replyIoDevice_.data() ) );
if ( !file->open( QIODevice::ReadOnly ) )
{
qDebug() << "JQHttpServer::Session::replyFile: open file error:" << filePath;
replyIoDevice_.clear();
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
replyBodySize_ = file->size();
const auto &&data = replyFileFormat
.arg(
QString::number( httpStatusCode ),
QFileInfo( filePath ).fileName(),
QString::number( replyBodySize_ ) )
.toUtf8();
waitWrittenByteCount_ = data.size() + file->size();
ioDevice_->write( data );
}
void JQHttpServer::Session::replyFile(const QString &fileName, const QByteArray &fileData, const int &httpStatusCode)
{
JQHTTPSERVER_SESSION_REPLY_PROTECTION( "replyFile" )
if ( QThread::currentThread() != this->thread() )
{
replyHttpCode_ = httpStatusCode;
QMetaObject::invokeMethod( this, "replyFile", Qt::QueuedConnection, Q_ARG( QString, fileName ), Q_ARG( QByteArray, fileData ), Q_ARG( int, httpStatusCode ) );
return;
}
JQHTTPSERVER_SESSION_REPLY_PROTECTION2( "replyFile" )
auto buffer = new QBuffer;
buffer->setData( fileData );
if ( !buffer->open( QIODevice::ReadWrite ) )
{
qDebug() << "JQHttpServer::Session::replyFile: open buffer error";
delete buffer;
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
replyIoDevice_.reset( buffer );
replyIoDevice_->seek( 0 );
replyBodySize_ = fileData.size();
const auto &&data =
replyFileFormat
.arg( QString::number( httpStatusCode ), fileName, QString::number( replyBodySize_ ) )
.toUtf8();
waitWrittenByteCount_ = data.size() + fileData.size();
ioDevice_->write( data );
}
void JQHttpServer::Session::replyImage(const QImage &image, const QString &format, const int &httpStatusCode)
{
JQHTTPSERVER_SESSION_REPLY_PROTECTION( "replyImage" )
if ( QThread::currentThread() != this->thread() )
{
replyHttpCode_ = httpStatusCode;
QMetaObject::invokeMethod( this, "replyImage", Qt::QueuedConnection, Q_ARG( QImage, image ), Q_ARG( QString, format ), Q_ARG( int, httpStatusCode ) );
return;
}
JQHTTPSERVER_SESSION_REPLY_PROTECTION2( "replyImage" )
auto buffer = new QBuffer;
if ( !buffer->open( QIODevice::ReadWrite ) )
{
qDebug() << "JQHttpServer::Session::replyImage: open buffer error";
delete buffer;
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
if ( !image.save( buffer, format.toLatin1().constData() ) )
{
qDebug() << "JQHttpServer::Session::replyImage: save image to buffer error";
delete buffer;
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
replyIoDevice_.reset( buffer );
replyIoDevice_->seek( 0 );
replyBodySize_ = buffer->buffer().size();
const auto &&data =
replyImageFormat
.arg( QString::number( httpStatusCode ), format.toLower(), QString::number( replyBodySize_ ) )
.toUtf8();
waitWrittenByteCount_ = data.size() + buffer->buffer().size();
ioDevice_->write( data );
}
void JQHttpServer::Session::replyImage(const QString &imageFilePath, const int &httpStatusCode)
{
JQHTTPSERVER_SESSION_REPLY_PROTECTION( "replyImage" )
if ( QThread::currentThread() != this->thread() )
{
replyHttpCode_ = httpStatusCode;
QMetaObject::invokeMethod( this, "replyImage", Qt::QueuedConnection, Q_ARG( QString, imageFilePath ), Q_ARG( int, httpStatusCode ) );
return;
}
JQHTTPSERVER_SESSION_REPLY_PROTECTION2( "replyImage" )
auto file = new QFile( imageFilePath );
if ( !file->open( QIODevice::ReadOnly ) )
{
qDebug() << "JQHttpServer::Session::replyImage: open buffer error";
delete file;
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
replyIoDevice_.reset( file );
replyIoDevice_->seek( 0 );
replyBodySize_ = file->size();
const auto &&data =
replyImageFormat
.arg( QString::number( httpStatusCode ), QFileInfo( imageFilePath ).suffix(), QString::number( replyBodySize_ ) )
.toUtf8();
waitWrittenByteCount_ = data.size() + file->size();
ioDevice_->write( data );
}
void JQHttpServer::Session::replyBytes(const QByteArray &bytes, const QString &contentType, const int &httpStatusCode)
{
JQHTTPSERVER_SESSION_REPLY_PROTECTION( "replyBytes" )
if ( QThread::currentThread() != this->thread( ))
{
replyHttpCode_ = httpStatusCode;
QMetaObject::invokeMethod(this, "replyBytes", Qt::QueuedConnection, Q_ARG(QByteArray, bytes), Q_ARG(QString, contentType), Q_ARG(int, httpStatusCode));
return;
}
JQHTTPSERVER_SESSION_REPLY_PROTECTION2( "replyBytes" )
auto buffer = new QBuffer;
buffer->setData( bytes );
if ( !buffer->open( QIODevice::ReadWrite ) )
{
qDebug() << "JQHttpServer::Session::replyBytes: open buffer error";
delete buffer;
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
replyIoDevice_.reset( buffer );
replyIoDevice_->seek( 0 );
replyBodySize_ = buffer->buffer().size();
const auto &&data =
replyBytesFormat
.arg( QString::number( httpStatusCode ), contentType, QString::number( replyBodySize_ ) )
.toUtf8();
waitWrittenByteCount_ = data.size() + buffer->buffer().size();
ioDevice_->write(data);
}
void JQHttpServer::Session::replyOptions()
{
JQHTTPSERVER_SESSION_REPLY_PROTECTION( "replyOptions" )
if ( QThread::currentThread() != this->thread() )
{
replyHttpCode_ = 200;
QMetaObject::invokeMethod( this, "replyOptions", Qt::QueuedConnection );
return;
}
JQHTTPSERVER_SESSION_REPLY_PROTECTION2( "replyOptions" )
replyBodySize_ = 0;
const auto &&buffer = replyOptionsFormat.toUtf8();
waitWrittenByteCount_ = buffer.size();
ioDevice_->write( buffer );
}
void JQHttpServer::Session::inspectionBufferSetup1()
{
if ( !headerAcceptedFinished_ )
{
forever
{
static QByteArray splitFlag( "\r\n" );
auto splitFlagIndex = receiveBuffer_.indexOf( splitFlag );
// 没有获取到分割标记,意味着数据不全
if ( splitFlagIndex == -1 )
{
// 没有获取到 method 但是缓冲区内已经有了数据,这可能是一个无效的连接
if ( requestMethod_.isEmpty() && ( receiveBuffer_.size() > 4 ) )
{
// qDebug() << "JQHttpServer::Session::inspectionBuffer: error0";
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
return;
}
// 如果未获取到 method 并且已经定位到了分割标记符,那么直接放弃这个连接
if ( requestMethod_.isEmpty() && ( splitFlagIndex == 0 ) )
{
// qDebug() << "JQHttpServer::Session::inspectionBuffer: error1";
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
// 如果没有获取到 method 则先尝试分析 method
if ( requestMethod_.isEmpty() )
{
auto requestLineDatas = receiveBuffer_.mid( 0, splitFlagIndex ).split( ' ' );
receiveBuffer_.remove( 0, splitFlagIndex + 2 );
if ( requestLineDatas.size() != 3 )
{
// qDebug() << "JQHttpServer::Session::inspectionBuffer: error2";
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
requestMethod_ = requestLineDatas.at( 0 );
requestUrl_ = requestLineDatas.at( 1 );
requestCrlf_ = requestLineDatas.at( 2 );
if ( ( requestMethod_ != "GET" ) &&
( requestMethod_ != "OPTIONS" ) &&
( requestMethod_ != "POST" ) &&
( requestMethod_ != "PUT" ) )
{
// qDebug() << "JQHttpServer::Session::inspectionBuffer: error3:" << requestMethod_;
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
}
else if ( splitFlagIndex == 0 )
{
receiveBuffer_.remove( 0, 2 );
headerAcceptedFinished_ = true;
if ( ( requestMethod_.toUpper() == "GET" ) ||
( requestMethod_.toUpper() == "OPTIONS" ) ||
( ( requestMethod_.toUpper() == "POST" ) && ( ( contentLength_ > 0 ) ? ( !receiveBuffer_.isEmpty() ) : ( true ) ) ) ||
( ( requestMethod_.toUpper() == "PUT" ) && ( ( contentLength_ > 0 ) ? ( !receiveBuffer_.isEmpty() ) : ( true ) ) ) )
{
this->inspectionBufferSetup2();
}
}
else
{
auto index = receiveBuffer_.indexOf( ':' );
if ( index <= 0 )
{
// qDebug() << "JQHttpServer::Session::inspectionBuffer: error4";
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
auto headerData = receiveBuffer_.mid( 0, splitFlagIndex );
receiveBuffer_.remove( 0, splitFlagIndex + 2 );
const auto &&key = headerData.mid( 0, index );
auto value = headerData.mid( index + 1 );
if ( value.startsWith( ' ' ) )
{
value.remove( 0, 1 );
}
requestHeader_[ key ] = value;
if ( key.toLower() == "content-length" )
{
contentLength_ = value.toLongLong();
}
}
}
}
else
{
this->inspectionBufferSetup2();
}
}
void JQHttpServer::Session::inspectionBufferSetup2()
{
requestBody_ += receiveBuffer_;
receiveBuffer_.clear();
if ( !handleAcceptedCallback_ )
{
qDebug() << "JQHttpServer::Session::inspectionBuffer: error4";
m_workingFuc.waitForFinished();
this->deleteLater();
return;
}
if ( ( contentLength_ != -1 ) && ( requestBody_.size() != contentLength_ ) )
{
return;
}
if ( contentAcceptedFinished_ )
{
return;
}
contentAcceptedFinished_ = true;
handleAcceptedCallback_( this );
}
void JQHttpServer::Session::onBytesWritten(const qint64 &written)
{
if ( this->waitWrittenByteCount_ < 0 ) { return; }
autoCloseTimer_->stop();
this->waitWrittenByteCount_ -= written;
if ( this->waitWrittenByteCount_ <= 0 )
{
this->waitWrittenByteCount_ = 0;
m_workingFuc.waitForFinished();
QTimer::singleShot( 500, this, &QObject::deleteLater );
return;
}
if ( !replyIoDevice_.isNull() )
{
if ( replyIoDevice_->atEnd() )
{
replyIoDevice_->deleteLater();
replyIoDevice_.clear();
}
else
{
ioDevice_->write( replyIoDevice_->read( 512 * 1024 ) );
}
}
autoCloseTimer_->start();
}
经过测试,不在出现系统崩溃。