QHttpServer 填坑
QHttpServer 是我从github 下载的一个开源库。前些时候,有兄弟拿着这个库做项目,总是崩溃,然后来找我。一开始还以为真这库有什么bug,仔细一看,是这兄弟自己没注意埋下的坑。
我这里用简短的代码,简单的重现他的问题。
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
http = new QHttpServer();
connect(http, &QHttpServer::newRequest, this, &MainWindow::request);
http->listen(QHostAddress::Any, 8087);
}
void MainWindow::request(QHttpRequest *req, QHttpResponse *resp)
{
m_req = req;
m_resp = resp;
connect(req, &QHttpRequest::data, this, &MainWindow::appendBody);
connect(req, &QHttpRequest::end, this, &MainWindow::end);
}
void MainWindow::appendBody(const QByteArray &body)
{
m_body += body;
}
void MainWindow::end()
{
qDebug()<< m_body;
m_resp->setHeader("Content-Length", QString::number(m_body.size()));
m_resp->writeHead(200);
m_resp->end(m_body);
m_req->deleteLater();
m_resp->deleteLater();
m_req = nullptr;
m_resp = nullptr;
}
首先是在构造函数中,创建 http 服务,监听8087 端口。然后在 request 槽函数中,绑定槽函数 appendBody 和 end 。 一切看似正常,对于小程序,并且是服务端与客户端一对一的时候,的确是没啥大问题。但当客户端多,且需要同时访问服务端的时候,会发生什么呢?
就像上面的图,大家同时访问服务器,但我们保存客户端请求的指针只有一个,这势必会造成冲突,引起混乱,崩溃也是意料之中。
所以为避免这种情况,我们需要一个专门处理客户端请求的类,当接收到客户端请求时,就新建一个这样的对象,去处理请求的数据,处理完成自动释放自己,每一个客户端的请求都有一个专门的对象负责,这样才不会打架嘛。
还有,网络拥堵,数据接收到一半,客户端和服务端的连接突然中断,导致服务器接收不到客户端发过来的end
的消息。当接收不到end
消息的时候,那处理该请求的对象,就会一直等待着,直到程序结束才会被释放。为了防止这种事情发生,我们还应该做一个请求计数,维护一个链表。当这个链表大于一定的数量(大于同时接收到请求的数量)时,将链表中最早的那个处理请求的对象给释放掉。
//mainwindow.cpp
QList<HandleRequest*> handleRequests;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
http = new QHttpServer();
connect(http, &QHttpServer::newRequest, this, &MainWindow::request);
http->listen(QHostAddress::Any, 8087);
}
void MainWindow::request(QHttpRequest *req, QHttpResponse *resp)
{
HandleRequest *handleRequest = new HandleRequest(req, resp, this);
handleRequest->setHandleRequests(&handleRequests);
handleRequests.append(handleRequest);
if(handleRequests.count() > 50) {
handleRequests.first()->deleteLater();
handleRequests.pop_front();
}
}
//handlerequest.cpp
HandleRequest::HandleRequest(QHttpRequest *req, QHttpResponse *resp, QObject *parent) :
QObject(parent),
m_req(req),
m_resp(resp)
{
m_req->storeBody();
connect(m_req, &QHttpRequest::end, this, &HandleRequest::end);
}
void HandleRequest::end()
{
QByteArray m_body = m_req->body();
qDebug()<< m_body;
m_resp->setHeader("Content-Length", QString::number(m_body.size()));
m_resp->writeHead(200);
m_resp->end(m_body);
m_req->deleteLater();
m_resp->deleteLater();
m_req = nullptr;
m_resp = nullptr;
m_handleRequests->removeOne(this);
this->deleteLater();
}
void HandleRequest::setHandleRequests(QList<HandleRequest *> *handleRequests)
{
m_handleRequests = handleRequests;
}