C++Qt建立一个HTTP服务器
- 开源代码
- 2025-08-26 05:09:02

1、下载http模块
上网查询发现在C++中,建立HTTP服务器几乎都是用的是http-parser,然后我也准备直接使用,但是如果要在Qt中使用,这还需要自己再次封装一遍,于是乎我找到了这个示例,非常好用:
legahero/HttpJsonServer: 用QT实现HTTP JSON服务器( http json server by QT) github /legahero/HttpJsonServer
打开这个项目目录,需要知道的有主窗口的源文件,以及config目录、httpbase目录,分析项目结构后不难发现,config是数据库配置文件所在的目录(你如果不需要,则完全可以不用管),httpbase是封装好了的http和数据库各种类的目录,详细的下面慢慢介绍。
2、引用http模块首先第一步是引用,上面地址的项目示例下载完成后,直接将httpbase目录copy到我们的项目目录中,然后像示例一样,在.pro文件中引用:
不要忘了加上Qt的网络模块(network)和数据库(sql)模块,如果你觉得每次建立项目都需要加上这么一堆,很麻烦,那就在httpbase目录中建立一个.pri文件,然后引用.pri文件就可以,例如:
#.pri文件内容 QT += network sql SOURCES += httpbase/qhttprequest.cpp \ httpbase/qhttpresponse.cpp \ httpbase/qhttpserver.cpp \ httpbase/http_parser.c \ httpbase/threadhandle.cpp \ httpbase/qasyntcpserver.cpp \ httpbase/qasynhttpsocket.cpp \ httpbase/staticfilecontroller.cpp \ httpbase/httphandler.cpp \ httpbase/qasyntcpsocket.cpp \ httpbase/qconnectpool.cpp \ httpbase/qmultidbmanager.cpp HEADERS += httpbase/http_parser.h \ httpbase/qhttprequest.h \ httpbase/qhttpresponse.h \ httpbase/threadhandle.h \ httpbase/qasyntcpserver.h \ httpbase/qasynhttpsocket.h \ httpbase/qhttpserver.h \ httpbase/staticfilecontroller.h \ httpbase/httphandler.h \ httpbase/qasyntcpsocket.h \ httpbase/qhttpserverfwd.h \ httpbase/qconnectpool.h \ httpbase/qmultidbmanager.h #.pri文件在.pro文件中引用 include(httpbase/httpbase.pri) 3、使用http模块首先看主窗口的源文件,就可以看到初始化http直接使用QHttpServer类就行了,不需要过多的动作,然后就找http的业务处理在哪里,在QHttpServer类中我们看定义的各种函数,一下就可以看到有个槽函数叫handleRequest,一看就知道是处理业务的函数,然后我们去看看示例中的业务处理代码如下,逐字逐句分析,这里使用注释讲解:
//处理新的http 请求,这里处理业务 void QHttpServer::handleRequest(QHttpRequest *req, QHttpResponse *resp) { qDebug() << "QHttpServer:handleRequest,ThreadId:"<<QThread::currentThreadId() ; qDebug() <<"path:"<< req->path()<<"body:"<<req->body();//打印请求路径和消息内容 qDebug() <<"headers:"<< req->headers();//打印请求头,是hash表类型的数据 if(req->path().indexOf('.')>=0)//如果请求的是根目录中的文件,则使用文件转发模块 { QString configFileName=searchConfigFile(); // Configure static file controller QSettings* fileSettings=new QSettings(configFileName,QSettings::IniFormat); fileSettings->beginGroup("docroot"); StaticFileController* staticFileController=new StaticFileController(fileSettings); //StaticFileController staticFileController(fileSettings); staticFileController->Handler(*req, *resp); return ; } QJsonDocument doc=QJsonDocument::fromBinaryData(req->body());//这里接收到的数据是二进制形式的数据,所以使用fromBinaryData函数,如果是utf8格式使用fromJson QJsonObject recv_obj=doc.object();//这是接收到的json对象 QConnectPool* dbpool=QMultiDbManager::getDb("sql2014");//获取sql2014数据库句柄 if(dbpool!=NULL) { QSqlDatabase db=dbpool->openSession(); QSqlQuery query(db); query.exec("SELECT top 1 * FROM tLogin ");//查询用户列表 QJsonObject resp_obj; //返回json对象 while (query.next()) { //将用户数据打包进返回json数据中 resp_obj.insert("LoginName",query.value("LoginName").toString()); resp_obj.insert("Pwd", query.value("LoginName").toString()); } dbpool->closeSession(db);//关闭数据库 QByteArray data = QJsonDocument(resp_obj).toJson(QJsonDocument::Compact); resp->setHeader("Content-Type", "text/html");//设置请求头,返回数据类型 resp->setHeader("Content-Length", QString::number(data.length()));//返回消息内容大小 resp->writeHead(200);//写入请求头,并带有返回码200表示请求成功 resp->end(data);//写入完毕,顺便带入最后的消息。可以去定义处看,一般写入使用函数write }else { qDebug() <<"get QMultiDbManager::getDb fail"; //resp->setHeader("Content-Type", "text/html"); resp->writeHead(403);//这里数据库打开失败了,返回403 } resp->flush();//开始回收资源 req->deleteLater(); resp->deleteLater(); qDebug() <<"handleRequest end"; }上述就是大概的使用流程了,关于请求业务类型的区分,查看QHttpRequest类的定义可以看到枚举类型HttpMethod,就可以知道有哪些业务请求类型了,然后两个函数获取当前请求的类型method()和methodString()。
4、使用过程中会遇到的问题(1)消息主体为空
打印req->body()没有消息,看程序输出日志readall有,说明解析后的消息内容没有添加到QHttpRequest类中,在哪里断层了,跟踪解析逻辑后来到QAsynHttpSocket类,这里是进行消息各个值赋值的地方,分析各个私有变量后发现当前临时的消息主体QByteArray m_currentBody,然后查看QHttpRequest中实例化了QAsynHttpSocket类同样是私有成员,所以为了直接能够在业务处理中打印m_currentBody,所以定义两个公有的函数:
//QAsynHttpSocket类中,这个直接复制在头文件的公有定义中 const QByteArray &getCurTempbody() const { return m_currentBody; } //QHttpRequest类中 QByteArray QHttpRequest::getCurTempbody() { if(m_connection == nullptr){ return ""; } return m_connection->getCurTempbody(); }然后调试打印,发现m_currentBody就是最新的那个消息内容,准确无误了,再次分析QAsynHttpSocket类,发现函数doMessageComplete()的功能就是构建QHttpRequest类,并传递出去,那我们就在这里添加消息主体内容,问题解决:
Q_EMIT request->data(theConnection->m_currentBody);//分析后发现该信号就是添加内容的,我们直接将当前临时的消息发送过去就行了 Q_EMIT theConnection->newRequest(request, response);//这是发送构建完成的信号,在这里之前添加内容(2)请求文件资源一直失败
测试访问文件资源一直失败,打印发现一直提示termiteHttpServer.ini文件未找到,这个时候也可以看到根目录为“./etc”,那我们就直接在这里创建termiteHttpServer.ini文件,因为跟踪查看,发现其实什么内容都不用添加,再访问仍旧失败,业务处理函数中,直接看staticFileController->Handler(*req, *resp);一句,这里转发文件,分析函数Handler后可以知道这里缺少请求头,消息内容的长度以及应答状态码,我们找到下面的第一行代码,然后将后续两行直接添加即可:
response.setHeader("Cache-Control","max-age=" + QByteArray::number(maxAge/1000)); response.setHeader("Content-Length", QString::number(file.size()));//文件大小 response.writeHead(200);//状态码C++Qt建立一个HTTP服务器由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“C++Qt建立一个HTTP服务器”