200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > C++练手项目(基于muduo网络库+mysql+jsoncpp的简易HTTPWebServer用于网页显示数据库后台数据

C++练手项目(基于muduo网络库+mysql+jsoncpp的简易HTTPWebServer用于网页显示数据库后台数据

时间:2021-08-10 10:24:11

相关推荐

C++练手项目(基于muduo网络库+mysql+jsoncpp的简易HTTPWebServer用于网页显示数据库后台数据

基于muduo网络库+mysql+jsoncpp的简易HTTPWebServer

项目介绍背景介绍主要模块介绍1、基于muduo网络库的WebServer:2、HTTP协议栈3、JsonServer:4、SQLConnection5、main模块 实现过程1、确定如何实现一个HTTPWebServer2、手动实现HTTP协议栈3、结合HTTP协议栈与muduo网络库实现HTTPWebServer4、JsonServer的实现:5、SQLConnection的实现:6、onRequest回调函数/简易cgi的实现:7、main函数的实现: 结果展示总结参考

项目介绍

项目github:github链接

本项目基于C++语言、陈硕老师的muduo网络库、mysql数据库以及jsoncpp,服务器监听两个端口,一个端口用于处理http请求、另一个端口用于处理发送来的json数据。

此项目是配合一个qt+opencv车牌识别前端程序,识别出车牌后打包为json数据发送给后端服务器保存在mysql数据库中并且显示在网页上。

背景介绍

本项目为个人学习所编写的小项目,没有非常细致地考虑到非常完整的HTTP协议栈等内容。可只监听80号端口使用POST方法来传输JSON数据,但是考虑到信息来源不一定实现了HTTP的POST协议,因此另监听一个端口只传输JSON数据比较方便。

主要模块介绍

1、基于muduo网络库的WebServer:

该WebServer基于muduo网络库与HTTP协议栈,muduo网络库主要负责socket连接的管理以及数据的管理,而在连接建立时,在连接的上下文context中初始化HTTPcontext,然后在接收到HTTP请求时,通过调用onMessage回调函数将接收到的数据保存在连接的HTTPcontext中并且处理HTTP请求,然后通过onRequest回调函数根据请求生成HTTP响应,在哈希表(与数据库同步)中获取所有车牌返回对应的HTML页面。

2、HTTP协议栈

HTTP协议栈主要包括HTTPContext, HTTPRequest, HTTPResponse类

(1) HTTPContext类:主要用于逐行处理、储存HTTPRequest(HTTP请求),然后该类再储存在muduo的TCPConnection(连接类)中供回调函数使用,处理HTTP请求,可以避免TCP粘包的情况。

(2) HTTPRequest类:利用枚举类型储存HTTP请求方法,hashmap来储存一个连接的HTTP头部,string来储存请求的body。

(3) HTTPResponse类:默认返回400 NotFound,需要在WebServer中设置HTTP的onRequest回调,手动根据HTTP请求构造HTTP响应,需要设置状态码、状态码描述、响应头部、实体。

3、JsonServer:

(1) JsonServer负责监听一个端口,在建立连接后,收到数据时,直接解析该Json是否完整,不完整就继续监听端口,并且将不完整的Json数据储存于连接的BUFFER中,在解析完整的Json表单后将BUFFER清空即可。

(2) 该server还包含一个SQLConnection,在解析到正确的Json后将车牌与ID储存于MySQL数据库中即可。

4、SQLConnection

通过RAII的手法控制SQL连接,保证SQL连接正确的生命周期,并且实现query、insert成员函数实现查询与插入的功能。

5、main模块

根据需求设置两个server的IP地址、端口、HTTP的onRequest的回调函数(生成符合需求的HTTP响应,可以理解为简易的cgi)

实现过程

1、确定如何实现一个HTTPWebServer

在刚开始项目时,需要确定如何用C++语言实现一个简单的HTTPWebServer,可以用基础的socket编程+手动实现HTTP协议栈从底层实现;可以使用现成的WebServer,如lighttpd、apache、nginx等服务器+cgi程序实现。但是最终我选择使用陈硕老师的muduo网络库+手动实现HTTP协议栈从应用层面实现一个WebServer,原因如下:首先之前我自己就已经从底层实现过一个WebServer(github:tinyWebServer),使用的是非阻塞IO模型+reactor模拟proactor的事件处理模式,因此并不想copy代码实现该项目,其次muduo网络库有非常多值得学习的细节,例如如何正确高效地管理socket连接、不同于我之前实现WebServer的模型,muduo网络库使用主reactor+从reactor,per thread one loop的模型的事件处理模式。

2、手动实现HTTP协议栈

PS:此处参考了nearXDU大佬的文章

实现一个简易HTTPWebServer光利用muduo网络库只能负责管理连接、数据,而实际应用层的HTTP协议栈需要自己实现,因此设计了HTTPContext, HTTPRequest, HTTPResponse。HTTPContext是每一个TCP连接对应的一次HTTP请求的一个上下文,包括请求,储存于muduo::net::TCPConnection的context中。HTTP解析的详细实现不必多说,HTTP协议是基础中的基础,代码中主要利用了C++内置的处理字符串的方法来解析HTTP请求,并利用unordered_map将解释出来的请求信息储存于HTTPRequest类的缓存中,而HTTPResponse则是专门为muduo网络库所设计,在设置好HTTP响应的请求行、请求头、实体后调用其appendToBuffer的成员函数然后从muduo网络库发送响应。代码如下:

httpcontext.h

#ifndef HTTPCONTEXT_H#define HTTPCONTEXT_H#include <iostream>#include <algorithm>#include <muduo/net/Buffer.h>#include "httprequest.h"using namespace std;using namespace muduo;using namespace muduo::net;class HttpContext{public:enum HttpRequestParseState{kExpectRequestLine,kExpectHeaders,kExpectBody,kGotAll};HttpContext():state_(kExpectRequestLine){}bool parseRequest(Buffer *buf, Timestamp receiveTime);bool gotAll() const{return state_ == kGotAll;}void reset(){state_ = kExpectRequestLine;HttpRequest dummy;request_.swap(dummy);}const HttpRequest& request() const{return request_;}private://解析请求行bool processRequestLine(const char* begin, const char* end);private:HttpRequestParseState state_;//解析结果保存在request_成员中HttpRequest request_;};#endif // HTTPCONTEXT_H

httpcontext.cpp:

#include "httpcontext.h"//解析请求行bool HttpContext::processRequestLine(const char *begin, const char *end){bool succeed = false;const char *start = begin;const char *space = find(start, end, ' ');//设置请求方法method_if(space != end && request_.setMethod(start, space)){start = space + 1;space = find(start, end, ' ');if(space != end){//解析URIconst char *question = find(start, space, '?');if(question != space){request_.setPath(start, question);request_.setQuery(question, space);}else{request_.setPath(start, space);}//解析HTTP版本号start = space + 1;succeed = end-start == 8 && equal(start, end-1, "HTTP/1.");if(succeed){if(*(end-1) == '1'){request_.setVersion(HttpRequest::HTTP11);}else if(*(end-1) == '0'){request_.setVersion(HttpRequest::HTTP10);}else{succeed = false;}}}}return succeed;}//解析请求头bool HttpContext::parseRequest(Buffer *buf, Timestamp receiveTime){bool ok = true;bool hasMore = true;while(hasMore){//解析请求行if(state_ == kExpectRequestLine){const char *crlf = buf->findCRLF();if(crlf){//开始解析请求行ok = processRequestLine(buf->peek(), crlf);if(ok){//解析成功request_.setReceiveTime(receiveTime);//回收请求行bufferbuf->retrieveUntil(crlf+2);state_ = kExpectHeaders;}else{hasMore = false;}}else{hasMore = false;}}//解析请求头else if(state_ == kExpectHeaders){const char *crlf = buf->findCRLF();if(crlf){//冒号const char *colon = find(buf->peek(), crlf, ':');if(colon != crlf){request_.addHeader(buf->peek(), colon, crlf);}else{//empty line, end of header//FIXME:state_ = kGotAll;hasMore = false;}buf->retrieveUntil(crlf+2);//回收}else{hasMore = false;}}else if(state_ == kExpectBody){cout << "HttpContext: parse body" << endl;}}//end whilereturn ok;}

httprequest.h

#ifndef HTTPREQUEST_H#define HTTPREQUEST_H#include <iostream>#include <muduo/base/Timestamp.h>#include <string>#include <unordered_map>using namespace std;using namespace muduo;class HttpRequest{public:enum Method{INVALID,GET,POST,HEAD,PUT,DELETE};enum Version{UNKNOWN,HTTP10,HTTP11};HttpRequest();void setVersion(Version v);Version getVersion() const;bool setMethod(const char *start, const char *end);Method method() const;const char* methodString() const;void setPath(const char* start, const char* end);const string& path() const;void setQuery(const char *start, const char *end);const string& query() const;void setReceiveTime(Timestamp t);Timestamp receiveTime() const;void addHeader(const char *start, const char *colon, const char *end);string getHeader(const string &field) const;const unordered_map<string,string>& headers() const;void swap(HttpRequest& that);private:Method method_;Version version_;string path_;string query_;Timestamp receiveTime_;unordered_map<string,string> headers_;};#endif // HTTPREQUEST_H

httprequest.cpp

#include "httprequest.h"HttpRequest::HttpRequest():method_(INVALID),version_(UNKNOWN){}void HttpRequest::setVersion(HttpRequest::Version v){version_ = v;}HttpRequest::Version HttpRequest::getVersion() const{return version_;}bool HttpRequest::setMethod(const char *start, const char *end){assert(method_ == INVALID);string m(start,end);if(m == "GET"){method_ = GET;}else if(m == "POST"){method_ = POST;}else if(m == "HEAD"){method_ = HEAD;}else if(m == "PUT"){method_ = PUT;}else if(m == "DELETE"){method_ = DELETE;}else{method_ = INVALID;}return method_ != INVALID;}HttpRequest::Method HttpRequest::method() const{return method_;}const char *HttpRequest::methodString() const{const char *result = "UNKNOWN";switch(method_){case GET:result = "GET";break;case POST:result = "POST";break;case HEAD:result = "HEAD";break;case PUT:result = "PUT";break;case DELETE:result = "DELETE";break;default:break;}return result;}void HttpRequest::setPath(const char *start, const char *end){path_.assign(start,end);}const string &HttpRequest::path() const{return path_;}void HttpRequest::setQuery(const char *start, const char *end){query_.assign(start,end);}const string &HttpRequest::query() const{return query_;}void HttpRequest::setReceiveTime(Timestamp t){receiveTime_ = t;}Timestamp HttpRequest::receiveTime() const{return receiveTime_;}void HttpRequest::addHeader(const char *start, const char *colon, const char *end){string field(start,colon);++colon;while(colon < end && isspace(*colon))++colon;string value(colon,end);while(!value.empty() && isspace(value[value.size()-1]))value.resize(value.size()-1);headers_[field] = value;}string HttpRequest::getHeader(const string &field) const{string result;unordered_map<string, string>::const_iterator it = headers_.find(field);if(it != headers_.end())result = it->second;return result;}const unordered_map<string, string> &HttpRequest::headers() const{return headers_;}void HttpRequest::swap(HttpRequest &that){std::swap(method_, that.method_);path_.swap(that.path_);query_.swap(that.query_);receiveTime_.swap(that.receiveTime_);headers_.swap(that.headers_);}

httpresponse.h

#ifndef HTTPRESPONSE_H#define HTTPRESPONSE_H#include <iostream>#include <string>#include <unordered_map>#include <muduo/net/Buffer.h>using namespace std;using namespace muduo;using namespace muduo::net;class HttpResponse{public:enum HttpStatusCode{CODE_UNKNOWN,CODE_200 = 200,CODE_301 = 301,CODE_400 = 400,CODE_404 = 404};explicit HttpResponse(bool close):statusCode_(CODE_UNKNOWN),closeConnection_(close){}void setStatusCode(HttpStatusCode code);void setStatusMessage(const string &message);void setCloseConnection(bool on);bool closeConnction() const;void setContentType(const string &contentType);void addHeader(const string &key, const string &value);void setBody(const string &body);void appendToBuffer(Buffer *output) const;private://响应头unordered_map<string,string> headers_;//响应码HttpStatusCode statusCode_;//状态信息string statusMessage_;//是否keep_alivebool closeConnection_;//响应报文string body_;};#endif // HTTPRESPONSE_H

httpresponse.cpp

#include "httpresponse.h"void HttpResponse::setStatusCode(HttpResponse::HttpStatusCode code){statusCode_ = code;}void HttpResponse::setStatusMessage(const string &message){statusMessage_ = message;}void HttpResponse::setCloseConnection(bool on){closeConnection_ = on;}bool HttpResponse::closeConnction() const{return closeConnection_;}void HttpResponse::setContentType(const string &contentType){addHeader("Content-Type", contentType);}void HttpResponse::addHeader(const string &key, const string &value){headers_[key] = value;}void HttpResponse::setBody(const string &body){body_ = body;}void HttpResponse::appendToBuffer(Buffer *output) const{char buf[32];//构造响应行snprintf(buf, sizeof(buf), "HTTP/1.1 %d ", statusCode_);output->append(buf);output->append(statusMessage_);output->append("\r\n");if(closeConnection_){output->append("Connection: close\r\n");}else{//Keep-Alive需要Content-Lengthsnprintf(buf, sizeof(buf), "Content-Length: %zd\r\n", body_.size());output->append(buf);output->append("Connection: Keep-Alive\r\n");}for(auto it = headers_.begin(); it != headers_.end(); ++it){output->append(it->first);output->append(": ");output->append(it->second);output->append("\r\n");}output->append("\r\n");//响应报文output->append(body_);}

3、结合HTTP协议栈与muduo网络库实现HTTPWebServer

PS:可以在此注册定时器来主动关闭超时链接

muduo网络库是基于对象的,基于其设计服务器需要基于对象,在Server类中包含muoduo::net::TcpServer,然后注册连接、接收、断开等时刻的回调函数实现服务器的基础功能,主要HTTP设计代码如下:

httpserver.h

#ifndef HTTPSERVER_H#define HTTPSERVER_H#include <muduo/net/TcpServer.h>#include <muduo/base/Logging.h>#include <muduo/net/EventLoop.h>#include <iostream>#include <functional>#include <string>#include <muduo/net/Buffer.h>#include "httpcontext.h"#include "httprequest.h"#include "httpresponse.h"using namespace std;using namespace muduo;using namespace muduo::net;class HttpServer{public://http回调函数typedef function<void(const HttpRequest&,HttpResponse*)> HttpCallback;//构造、析构函数explicit HttpServer(EventLoop* loop,const InetAddress& listenAddr);~HttpServer();EventLoop* getLoop() const {return server_.getLoop(); }void setHttpCallback(const HttpCallback& cb){httpCallback_ = cb;}void setThreadNum(const int numThreads){server_.setThreadNum(numThreads);}void start();private:void onConnection(const TcpConnectionPtr &conn);void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp receiveTime);void onRequest(const TcpConnectionPtr &conn,const HttpRequest&);private:TcpServer server_;HttpCallback httpCallback_;};#endif // HTTPSERVER_H

httpserver.cpp

#include "httpserver.h"void defaultHttpCallback(const HttpRequest&,HttpResponse* resp){resp->setStatusCode(HttpResponse::CODE_400);resp->setStatusMessage("Not Found");resp->setCloseConnection(true);}HttpServer::HttpServer(EventLoop *loop, const InetAddress &listenAddr):server_(loop, listenAddr, "wyeHttpServer"), httpCallback_(defaultHttpCallback){server_.setConnectionCallback(std::bind(&HttpServer::onConnection, this, placeholders::_1));server_.setMessageCallback(std::bind(&HttpServer::onMessage, this, placeholders::_1, placeholders::_2, placeholders::_3));}HttpServer::~HttpServer(){}void HttpServer::start(){LOG_WARN << "HttpServer[" << server_.name() << "] starts listenning on " << server_.ipPort();server_.start();}//新连接回调void HttpServer::onConnection(const TcpConnectionPtr &conn){if(conn->connected()){conn->setContext(HttpContext());}}//消息回调void HttpServer::onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp receiveTime){HttpContext *context = boost::any_cast<HttpContext>(conn->getMutableContext());//解析请求if(!context->parseRequest(buf, receiveTime)){conn->send("HTTP/1.1 400 Bad Request\r\n\r\n");conn->shutdown();}if(context->gotAll()){//请求解析完毕onRequest(conn, context->request());context->reset();}}void HttpServer::onRequest(const TcpConnectionPtr &conn, const HttpRequest &req){const string &connection = req.getHeader("Connection");bool close = connection == "close" || (req.getVersion() == HttpRequest::HTTP10 && connection != "Keep-Alive");HttpResponse response(close);//构造响应httpCallback_(req, &response);Buffer buf;//此时response已经构造好,将向客户发送response添加到buffer中response.appendToBuffer(&buf);conn->send(&buf);//如果非Keep-Alive就关闭if(response.closeConnction()){conn->shutdown();}}

4、JsonServer的实现:

解析Json有很多方法,最初想使用Boost库来解析,但是在1.75版本前解析Json需要用到boost::property_tree,操作较为复杂,一段优秀的代码应该是使用更好的轮子写出来的,并且会是通俗易懂的。因此推荐,并且我也使用了Jsoncpp库来解析Json数据。然后JsonServer中还包含了SQLConnection,用于储存解析的Json数据到MySQL中。而具体实现还是基于muduo网络库,与HTTPServer类的设计大同小异,主要Json服务器设计代码如下:

jsonprocess.h

#ifndef JSONPROCESS_H#define JSONPROCESS_H#include <muduo/net/EventLoop.h>#include <muduo/net/TcpServer.h>#include <muduo/net/Buffer.h>#include <muduo/base/Logging.h>#include <functional>#include <map>#include <unordered_set>#include <jsoncpp/json/json.h>#include "sqlconnection.h"using namespace muduo;using namespace muduo::net;using namespace std;class JsonProcess{public:explicit JsonProcess(EventLoop *loop,const InetAddress& listenAddr, map<int,vector<string>> &mp);~JsonProcess(){sqlConnection_.disconnectFromSqlServer();}void start();private:void onConnection(const TcpConnectionPtr &conn);void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp t);void parseJson(Buffer *buf, string ip);const map<int,vector<string>>& getJsonMap(){return jsonMap;}public:int totalId;private:TcpServer server_;SqlConnection sqlConnection_;map<int,vector<string>> &jsonMap;unordered_set<string> st;};#endif // JSONPROCESS_H

jsonprocess.cpp

#include "jsonprocess.h"#include <iostream>JsonProcess::JsonProcess(EventLoop *loop, const InetAddress &listenAddr, map<int,vector<string>> &mp):server_(loop, listenAddr, "JsonProcess"), jsonMap(mp),totalId(0){server_.setMessageCallback(std::bind(&JsonProcess::onMessage, this, placeholders::_1, placeholders::_2, placeholders::_3));server_.setConnectionCallback(std::bind(&JsonProcess::onConnection, this, placeholders::_1));sqlConnection_.connectToSqlServer();sqlConnection_.query(jsonMap, totalId);// for(auto it=jsonMap.begin(); it!=jsonMap.end(); ++it)// {// st.insert(it->second);// }}void JsonProcess::start(){LOG_INFO << "HttpServer[" << server_.name() << "] starts listenning on " << server_.ipPort();server_.start();}void JsonProcess::onConnection(const TcpConnectionPtr &conn){LOG_INFO << "New JsonProcess Connection: " << conn->peerAddress().toIpPort();}void JsonProcess::onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp t){LOG_INFO << "New Json Message";parseJson(buf, conn->peerAddress().toIp());}void JsonProcess::parseJson(Buffer *buf, string ip){const char *str = buf->peek();//不处理粘包情况FIX ME:string jsonStr(str, str+buf->readableBytes());//解析jsonJson::Value jsonRoot;Json::Reader jsonReader;if(!jsonReader.parse(jsonStr, jsonRoot)){LOG_WARN << "Json Message is not completed";return ;}// int id = jsonRoot["id"].asInt(); //弃置int id = totalId++;string license_plate = jsonRoot["license_plate"].asString();LOG_INFO << "parse json result:" << id << ":" << license_plate;//查重// if(st.find(license_plate) != st.end())// {// LOG_WARN << "license already existed!!";// buf->retrieveAll();// return ;// }// else// st.insert(license_plate);long long myTime = Timestamp::now().secondsSinceEpoch();Timestamp t((myTime+28800)*1e6);//插入到mysqlif(!sqlConnection_.insert(id, license_plate, myTime, ip)){LOG_WARN << "insert to sql fail!!";return ;}//插入到mapjsonMap[id] = vector<string>{license_plate, t.toFormattedString(), ip};LOG_INFO << id << ":" << license_plate;buf->retrieveAll();}

5、SQLConnection的实现:

主要用得到RAII的手段,保证SQL连接的生命周期,查询、插入操作则是很标准的C++SQL操作,SQLConnection主要代码如下:

sqlconnection.h

#ifndef SQLCONNECTION_H#define SQLCONNECTION_H#include <map>#include <vector>#include <string>#include <muduo/base/Logging.h>#include <mysql/mysql.h>using namespace std;using namespace muduo;class SqlConnection{public:SqlConnection(string server = "localhost", string user = "wye", string password = "nimabi123", string database = "license_plate");~SqlConnection();bool connectToSqlServer();bool query(map<int,vector<string>> &mp, int& totalId);bool insert(int id, string license_plate, long long myTime, string ip);void disconnectFromSqlServer();private:MYSQL *conn_;//string server_;string user_;string password_;string database_;//未使用bool isConnected_;//MYSQL_RES *res;//MYSQL_ROW row;//int cols;};#endif // SQLCONNECTION_H

sqlconnection.cpp

#include "sqlconnection.h"SqlConnection::SqlConnection(string server, string user, string password, string database):server_(server), user_(user), password_(password), database_(database), isConnected_(false){conn_ = mysql_init(NULL);}SqlConnection::~SqlConnection(){disconnectFromSqlServer();}bool SqlConnection::connectToSqlServer(){if(!mysql_real_connect(conn_, server_.c_str(), user_.c_str(), password_.c_str(), database_.c_str(), 0, NULL, 0)){LOG_WARN << mysql_error(conn_);return false;}if(mysql_query(conn_, "use licensePlate;")){LOG_WARN << mysql_error(conn_);return false;}if(mysql_query(conn_, "set names utf8")){LOG_WARN << mysql_error(conn_);return false;}if(mysql_query(conn_, "select * from cars;")){LOG_WARN << mysql_error(conn_);return false;}res = mysql_store_result(conn_);//深拷贝cols = mysql_num_fields(res);mysql_free_result(res);LOG_WARN << "Connect to MYSQL";return true;}bool SqlConnection::query(map<int, vector<string>> &mp, int& totalId){if(mysql_query(conn_, "SELECT id,license_plate,unix_timestamp(time),ip FROM cars;")){LOG_WARN << mysql_error(conn_);return false;}res = mysql_store_result(conn_);//深拷贝// int rows = mysql_num_rows(res);while((row = mysql_fetch_row(res)) != NULL){int id;string license_plate;Timestamp myTime;string ip;for(int i=0;i<cols;++i){switch(i){case 0:{id = atoi(row[i]);break;}case 1:{license_plate = row[i];break;}case 2:{Timestamp t((atoll(row[i]) + 28800)*10e5);//LOG_INFO << t.secondsSinceEpoch()-28800;myTime.swap(t);break;}case 3:{ip = row[i];break;}}}mp[id] = vector<string>{license_plate, myTime.toFormattedString(), ip};totalId = ++id;}mysql_free_result(res);return true;}bool SqlConnection::insert(int id, string license_plate, long long myTime, string ip){// time_t myTime = Timestamp::now().secondsSinceEpoch();string sqlInsert = "insert into cars(license_plate,time,ip) values(\"";sqlInsert += license_plate;sqlInsert += "\",FROM_UNIXTIME(";sqlInsert += to_string(myTime);sqlInsert += "),\"";sqlInsert += ip;sqlInsert += "\");";if(mysql_query(conn_, sqlInsert.c_str())){LOG_WARN << mysql_error(conn_);return false;}return true;}void SqlConnection::disconnectFromSqlServer(){mysql_free_result(res);mysql_close(conn_);LOG_WARN << "Disconnect from sqlserver";}

6、onRequest回调函数/简易cgi的实现:

在JsonWebServer初始化阶段会将本地内存的车牌哈希表进行相应的初始化,而新来的Json数据则会更新本地哈希表。然后具体onRequest回调函数中,生成的HTML页面会将本地哈希表的所有车牌插入其中,实现将数据库的车牌显示到浏览器网页的功能,并且使用unordered_set来进行去重操作。

回调函数(简易cgi):

void onRequest(const HttpRequest& req, HttpResponse *resp){if(req.method() != HttpRequest::GET){resp->setCloseConnection(true);resp->setStatusCode(HttpResponse::CODE_400);resp->setStatusMessage("Bad Request");return ;}string body;ifstream inFile;string path = req.path();int it = path.find('.');if(it != string::npos){inFile.open("beijing.jpg", ios_base::in | ios_base::binary);resp->setContentType("image/jpg");if(!inFile.is_open()){resp->setStatusCode(HttpResponse::CODE_404);resp->setStatusMessage("Not Found");resp->setCloseConnection(true);return ;}char buf[1024];memset(buf, 0, sizeof(buf));while(!inFile.eof()){inFile.read(buf,sizeof(buf));body += string(buf,buf+sizeof(buf));memset(buf, 0, sizeof(buf));}inFile.close();}else{body += "<html><head><meta charset=\"utf8\"><title>车牌识别系统</title></head><body background=\"beijing.jpg\"><center><h1 style=\"color:#97CBFF;font-size:60px;\">车牌识别系统</h1></center><div style=\"width:15%;height:100px;float:left;\"></div><div style=\"width:70%;background-color:rgba(0,0,0,0.5);float:left;\"><table border=\"1\" border-color=\"#97CBFF\" style=\"width:100%;color:white;\"><tr><th>id号</th><th>车牌号码</th><th>时间</th><th>IP地址</th></tr>";int idx = 1;for(auto it=globalMap.begin();it!=globalMap.end();++it){body += "<tr>";body += string("<td>") + to_string(idx++) + "</td>";body += string("<td>") + (it->second)[0] + "</td>";body += string("<td>") + (it->second)[1] + "</td>";body += string("<td>") + (it->second)[2] + "</td>";body += "</tr>";}body += "</table></div><div style = \"width:15%;height:100px;float:left;\"></div></body></html>";resp->setContentType("text/html");}// body += "<html><head><meta charset = \"utf8\"><title>车牌识别</title></head><center><h1>车牌系统</h1></center><ol style=\"color:blue\">";// for(auto it=globalMap.begin();it!=globalMap.end();++it)// {// body += string("<li>") + (it->second).first + " " + (it->second).second + "</li>";// }// body += "</ol><body></body></html>";resp->setBody(body);resp->setStatusCode(HttpResponse::CODE_200);resp->setStatusMessage("OK");}

7、main函数的实现:

根据muduo网络库的要求,创建loop对象,绑定loop对象到服务器类中,然后根据需求设置两个server的IP地址、端口、HTTP的onRequest的回调函数(生成符合需求的HTTP响应,可以理解为简易的cgi),最后start服务器并且启动loop即可。

main.cpp

#include <iostream>#include <fstream>#include <string>#include <map>#include "muduo/base/Logging.h"#include "muduo/net/EventLoop.h"#include "httpserver.h"#include "httpresponse.h"#include "httprequest.h"#include "jsonprocess.h"using namespace muduo;using namespace muduo::net;using namespace std;map<int,vector<string>> globalMap;void onRequest(const HttpRequest& req, HttpResponse *resp){if(req.method() != HttpRequest::GET){resp->setCloseConnection(true);resp->setStatusCode(HttpResponse::CODE_400);resp->setStatusMessage("Bad Request");return ;}string body;ifstream inFile;string path = req.path();int it = path.find('.');if(it != string::npos){inFile.open("beijing.jpg", ios_base::in | ios_base::binary);resp->setContentType("image/jpg");if(!inFile.is_open()){resp->setStatusCode(HttpResponse::CODE_404);resp->setStatusMessage("Not Found");resp->setCloseConnection(true);return ;}char buf[1024];memset(buf, 0, sizeof(buf));while(!inFile.eof()){inFile.read(buf,sizeof(buf));body += string(buf,buf+sizeof(buf));memset(buf, 0, sizeof(buf));}inFile.close();}else{body += "<html><head><meta charset=\"utf8\"><title>车牌识别系统</title></head><body background=\"beijing.jpg\"><center><h1 style=\"color:#97CBFF;font-size:60px;\">车牌识别系统</h1></center><div style=\"width:15%;height:100px;float:left;\"></div><div style=\"width:70%;background-color:rgba(0,0,0,0.5);float:left;\"><table border=\"1\" border-color=\"#97CBFF\" style=\"width:100%;color:white;\"><tr><th>id号</th><th>车牌号码</th><th>时间</th><th>IP地址</th></tr>";int idx = 1;for(auto it=globalMap.begin();it!=globalMap.end();++it){body += "<tr>";body += string("<td>") + to_string(idx++) + "</td>";body += string("<td>") + (it->second)[0] + "</td>";body += string("<td>") + (it->second)[1] + "</td>";body += string("<td>") + (it->second)[2] + "</td>";body += "</tr>";}body += "</table></div><div style = \"width:15%;height:100px;float:left;\"></div></body></html>";resp->setContentType("text/html");}// body += "<html><head><meta charset = \"utf8\"><title>车牌识别</title></head><center><h1>车牌系统</h1></center><ol style=\"color:blue\">";// for(auto it=globalMap.begin();it!=globalMap.end();++it)// {// body += string("<li>") + (it->second).first + " " + (it->second).second + "</li>";// }// body += "</ol><body></body></html>";resp->setBody(body);resp->setStatusCode(HttpResponse::CODE_200);resp->setStatusMessage("OK");}int main(){LOG_INFO << "Hello muduo!";int numThreads = 2;EventLoop loop;HttpServer server(&loop, InetAddress("192.168.137.208", 51234));server.setHttpCallback(onRequest);server.setThreadNum(numThreads);server.start();JsonProcess jsonProcess(&loop, InetAddress("192.168.137.208", 51233), globalMap);jsonProcess.start();loop.loop();return 0;}

结果展示

总结

本次分享的小项目使用了muduo网络库,C++的网络库本来就比较稀有,仅有muduo、boost.Asio、libevent、ACE这几个,而实际使用muduo网络库来编写的项目就非常稀少,因此这次这个项目可以有抛砖引玉的作用,为有兴趣研究网络编程的同学提供一个以muduo网络库为基础的项目实例。

参考

《Linux多线程服务端编程:使用muduo C++网络库》 ——陈硕

从零开始学写HTTP服务器(六)使用muduo网络库

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。