目錄
編寫http_server模塊
1. 引入cpp-httplib到項目中
2. cpp-httplib的使用介紹
3. 正式編寫http_server
九、添加日志到項目中
十、編寫前端模塊
十一. 詳解傳 gitee
十二、項目總結
項目的擴展
寫在前面
[項目詳解][boost搜索引擎#1] 概述 | 去標簽 | 數據清洗 | scp
[項目詳解][boost搜索引擎#2] 建立index | 安裝分詞工具cppjieba | 實現倒排索引
[項目][boost搜索引擎#3] Searcher模塊 | 單例設計 | 去重 | 構建json
項目 gitee 已經上傳啦
(還是決定將學校和個人的 gitee 區分開來,所以之后寫博客的代碼就用這個 gitee 號了(???)
https://gitee.com/linxi-lalala
接著上一篇文章,下面繼續講解網絡庫的使用~
編寫http_server模塊
1. 引入cpp-httplib到項目中
安裝cpp-httplib 安裝的是v0.7.15版本:
下載鏈接:cpp-httplib 下載地址
我們將cpp-httplib放到項目中的test目錄下,并解壓 unzip 好
建立軟連接到我們的項目路徑下:
注意:要使用 cpp-httplib ,我們的 gcc 的版本必須時 7 以上哦
至此,我們就可以在我們的項目中使用了。
2. cpp-httplib的使用介紹
創建一個http_server.cpp的文件,編寫測試代碼:
#include "cpp-httplib/httplib.h" int main()
{ //創建一個Server對象,本質就是搭建服務端httplib::Server svr; // 這里注冊用于處理 get 請求的函數,當收到對應的get請求時(請求hi時),程序會執行對應的函數(也就是lambda表達式)svr.Get("/hi", [](const httplib::Request& req, httplib::Response& rsp){ //設置 get "hi" 請求返回的內容 rsp.set_content("hello world!", "text/plain; charset=utf-8"); }); // 綁定端口(8080),啟動監聽(0.0.0.0表示監聽任意端口)svr.listen("0.0.0.0", 8080); return 0;
}
對應的Makefile:
PARSER=parser
DUG=debug
HTTP_SERVER=http_server
cpp=g++.PHONY:all
all:$(PARSER) $(DUG) $(HTTP_SERVER)$(PARSER):parser.cpp$(cpp) -o $@ $^ -lboost_system -lboost_filesystem -std=c++11
$(DUG):debug.cpp$(cpp) -o $@ $^ -std=c++11 -ljsoncpp
$(HTTP_SERVER):http_server.cpp$(cpp) -o $@ $^ -std=c++11 -ljsoncpp -lpthread
.PHONY:clean
clean:rm -f $(DUG) $(PARSER) $(HTTP_SERVER)
我們直接編譯運行 http_server
打開瀏覽器,訪問我們這個端口(如服務器IP:3877/hi),結果如下:
但是當我們訪問服務器IP:3877時,卻找不到對應的網頁,
像我們訪問百度時,www.baidu.com,百度會給一個首頁,所以在我們的項目目錄下呢,也需要一個首頁。 (在項目路徑下創建一個wwwroot目錄,目錄中包含一個index.html文件)
編寫我們的首頁,并修改我們的 http_server.cpp:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>boost搜索引擎</title>
</head>
<body><h1>歡迎來到我的世界</h1>
</body>
</html>#include "cpp-httplib/httplib.h"const std::string root_path = "./wwwroot";int main()
{ //創建一個Server對象,本質就是搭建服務端httplib::Server svr; //訪問首頁svr.set_base_dir(root_path.c_str());// 這里注冊用于處理 get 請求的函數,當收到對應的get請求時(請求hi時),程序會執行對應的函數(也就是lambda表達式)svr.Get("/hi", [](const httplib::Request& req, httplib::Response& rsp){ //設置 get "hi" 請求返回的內容 rsp.set_content("hello world!", "text/plain; charset=utf-8"); }); // 綁定端口(8080),啟動監聽(0.0.0.0表示監聽任意端口)svr.listen("0.0.0.0", 8080); return 0;
}
再次通過瀏覽器進行訪問:
3. 正式編寫http_server
#include "cpp-httplib/httplib.h"
#include "searcher.hpp" const std::string input = "data/raw_html/raw.txt";
const std::string root_path = "./wwwroot"; int main()
{ ns_searcher::Searcher search; search.InitSearcher(input); //創建一個Server對象,本質就是搭建服務端httplib::Server svr; //訪問首頁svr.set_base_dir(root_path.c_str()); // 這里注冊用于處理 get 請求的函數,當收到對應的get請求時(請求s時),程序會執行對應的函數(也就是lambda表達式)svr.Get("/s", [&search](const httplib::Request &req, httplib::Response &rsp){//has_param:這個函數用來檢測用戶的請求中是否有搜索關鍵字,參數中的word就是給用戶關鍵字取的名字(類似word=split) if(!req.has_param("word")){ rsp.set_content("必須要有搜索關鍵字!", "text/plain; charset=utf-8"); return; } //獲取用戶輸入的關鍵字std::string word = req.get_param_value("word"); std::cout << "用戶在搜索:" << word << std::endl; //根據關鍵字,構建json串std::string json_string; search.Search(word, &json_string);//設置 get "s" 請求返回的內容,返回的是根據關鍵字,構建json串內容rsp.set_content(json_string, "application/json"); }); std::cout << "服務器啟動成功......" << std::endl; // 綁定端口(8080),啟動監聽(0.0.0.0表示監聽任意端口)svr.listen("0.0.0.0", 3877); return 0;
}
此時我們編譯運行我們的代碼,先執行parser進行數據清洗,然后執行http_server,搭建服務,創建單例,構建索引,發生請求(根據用戶輸入的關鍵字,進行查找索引,構建json串),最后響應給用戶
此時服務器啟動成功,索引也建立完畢 ,
此時,我們在瀏覽器進行訪問(服務器IP:3877/s)
此時,我們在瀏覽器進行訪問(服務器IP:3877/sword=split)
最終,在瀏覽器上就顯示出來了,到這里我們的后端內容大致上算是完成了,最后添加一個日志就可以了,如果你對前端不感興趣,到這里就可以了。可以把日志功能的添加看一看
九、添加日志到項目中
我們創建一個log.hpp的頭文件,需要添加日志的地方:index模塊,searcher模塊、http_server模塊。代碼如下:
#pragma once
#include <iostream>
#include <string>
#include <ctime> #define NORMAL 1 //正常的
#define WARNING 2 //錯誤的
#define DEBUG 3 //bug
#define FATAL 4 //致命的 #define LOG(LEVEL, MESSAGE) log(#LEVEL, MESSAGE, __FILE__, __LINE__) void log(std::string level, std::string message, std::string file, int line)
{ // 打印日志信息std::cout << "[" << level << "]"<< "[" << time(nullptr) << "]"<< "[" << message << "]"<< "[" << file << " : " << line << "]"<< std::endl;
}
簡單說明:
我們用宏來實現日志功能,其中LEVEL表明的是等級(有四種),
這里的#LEVEL的作用是:把一個宏參數變成對應的字符串(直接替換)
C語言中的預定義符號:
- __FILE__:進行編譯的源文件
- __LINE__:文件的當前行號
補充幾個:
- __DATE__:文件被編譯的日期
- __TIME__:文件被編譯的時間
- __FUNCTION__:進行編譯的函數
假設在如下示例代碼:
int main() {LOG(NORMAL, "This is a normal log message");LOG(WARNING, "This is a warning log message");LOG(DEBUG, "This is a debug log message");LOG(FATAL, "This is a fatal log message");return 0;
}
編譯并運行這段代碼后,輸出會類似于:
所以我們可以將日志添加到有輸出入口的地方,方便監視我們的代碼那里出現了問題。
日志系統的作用
- 調試和錯誤追蹤:記錄程序執行過程中的各種狀態和錯誤信息,方便定位和修復問題。
- 運行監控:監控程序的運行狀態,了解程序的執行流程和重要事件。
- 審計和分析:分析日志記錄,了解用戶行為和系統性能,進行數據挖掘和改進。
十、編寫前端模塊
前端模塊,我做詳細的解釋,代碼中都有注釋,直接上代碼
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script src="http://code.jquery.com/jquery-2.1.1.min.js"></script><title>boost 搜索引擎</title><style>/*去掉網頁中的所有內外邊距,可以了解html的盒子模型*/* {margin: 0;/* 設置外邊距 */padding: 0;/* 設置內邊距 */}/* 將我們的body內的內容100%和html的呈現吻合 */html,body {height: 100%;}/* 以點開頭的稱為類選擇器.container */.container {/* 設置div的寬度 */width: 800px;/* 通過設置外邊距達到居中對其的目的 */margin: 0px auto;/* 設置外邊距的上邊距,保持元素和網頁的上部距離 */margin-top: 15px;}/* 復合選擇器,選中container下的search */.container .search {/* 寬度與父標簽保持一致 */width: 100%;/* 高度設置52px */height: 50px;}/* 選中input標簽,直接設置標簽的屬性,先要選中,標簽選擇器 *//* input在進行高度設置的時候,沒有考慮邊框的問題 */.container .search input {/* 設置left浮動 */float: left;width: 600px;height: 50px;/* 設置邊框屬性,依次是邊框的寬度、樣式、顏色 */border: 2px solid #CCC;/* 去掉input輸入框的右邊框 */border-right: none;/* 設置內內邊距,默認文字不要和左側邊框緊挨著 */padding-left: 10px;/* 設置input內部的字體的顏色和樣式 */color: #CCC;color: #CCC;font-size: 17px;}.container .search button {/* 設置left浮動 */float: left;width: 150px;height: 54px;/* 設置button的背景顏色 #4e6ef2*/background-color: #4e6ef2;color: #FFF;/* 設置字體的大小 */font-size: 19px;font-family: Georgia, 'Times New Roman', Times, serif 'Times New Roman', Times, serif;}.container .result {width: 100%;}.container .result .item {margin-top: 15px;}.container .result .item a {/* 設置為塊級元素,單獨占一行 */display: block;text-decoration: none;/* 設置a標簽中的文字字體大小 */font-size: 22px;/* 設置字體的顏色 */color: #4e6ef2;}.container .result .item a:hover {/* 設置鼠標放在a之上的動態效果 */text-decoration: underline;}.container .result .item p {margin-top: 5px;font-size: 16px;font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;}.container .result .item i {/* 設置為塊級元素,單獨占一行 */display: block;/* 取消斜體風格 */ font-style: normal;color: green;}</style>
</head><body><div class="container"><div class="search"><input type="text" value="輸入搜索關鍵字..."><button onclick="Search()">搜索一下</button></div><div class="result"><!-- <div class="item"><a href="#">這是標題</a><p>這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要</p><i>https://hao.360.com/?hj=llq7a</i></div><div class="item"><a href="#">這是標題</a><p>這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要</p><i>https://hao.360.com/?hj=llq7a</i></div><div class="item"><a href="#">這是標題</a><p>這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要</p><i>https://hao.360.com/?hj=llq7a</i></div><div class="item"><a href="#">這是標題</a><p>這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要</p><i>https://hao.360.com/?hj=llq7a</i></div><div class="item"><a href="#">這是標題</a><p>這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要</p><i>https://hao.360.com/?hj=llq7a</i></div><div class="item"><a href="#">這是標題</a><p>這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要這是摘要</p><i>https://hao.360.com/?hj=llq7a</i></div> --></div></div><script>function Search() {// 是瀏覽器的一個彈出窗// 1.提取數據,$可以理解為就是JQuery的別稱let query = $(".container .search input").val();console.log("query = " + query); //console是瀏覽器對話框,可以用來進行查看js數據// 2.發起http請求,ajax屬于一個和后端進行數據交互的函數$.ajax({type: "GET",url: "/s?word=" + query,success: function (data) {console.log(data);BuildHtml(data);}});}function BuildHtml(data) {// 獲取html中的result標簽let result_lable = $(".container .result");// 清空歷史搜索結果result_lable.empty();for (let elem of data) {console.log(elem.title);console.log(elem.url);let a_lable = $("<a>", {text: elem.title,href: elem.url,// 跳轉到新的頁面target: "_blank"});let p_lable = $("<p>", {text: elem.desc});let i_lable = $("<p>", {text: elem.url});let div_lable = $("<div>", {class: "item"});a_lable.appendTo(div_lable);p_lable.appendTo(div_lable);i_lable.appendTo(div_lable);div_lable.appendTo(result_lable);}} </script>
</body></html>
最終演示:
- 將我們的項目部署到Linux上:
n**ohup ./http_server > log/log.txt 2>&1 &**
- 一些日志信息就會保存到log/log.txt中
十一. 詳解傳 gitee
在 Ubuntu 上向 Gitee(碼云)上傳代碼,通常需要通過 Git 進行。下面是具體步驟:
-
安裝 Git:
如果你還沒有安裝 Git,可以通過以下命令來安裝:sudo apt-get update
sudo apt-get install git -
配置 Git:
首次使用 Git 時,需要設置你的用戶名和郵箱地址。這將用于提交信息。git config --global user.name “你的用戶名”
git config --global user.email “你的郵箱@example.com” -
創建 Gitee 倉庫:
登錄到 Gitee 并創建一個新的倉庫。記下倉庫的 URL。 -
初始化本地倉庫(如果你還沒有一個本地 Git 倉庫):
-
- 打開終端并導航到你的項目目錄。
- 初始化一個新的 Git 倉庫:
git init
-
- 添加遠程倉庫鏈接(替換
your-repo-url
為你的 Gitee 倉庫 URL):
git remote add origin your-repo-url
- 添加遠程倉庫鏈接(替換
- 添加文件到倉庫:
-
將所有文件添加到暫存區:
git add .
-
- 或者你可以選擇性地添加特定文件:
git add 文件名
- 提交更改:
-
提交文件到本地倉庫,并附上提交信息:
git commit -m “初始提交或描述本次提交的內容”
- 推送到 Gitee:
-
推送你的本地分支到 Gitee 倉庫的主分支(通常是
master
或main
):git push -u origin master
-
- 如果你的默認分支是
main
而不是master
,請相應地調整命令
git push -u origin main
- 如果你的默認分支是
- 驗證:
- 打開 Gitee 網站上的倉庫頁面,確認文件已經成功上傳。
如果你在推送過程中遇到權限問題,確保你在 Gitee 上正確設置了 SSH 密鑰或使用了正確的 HTTPS 憑證。
- 如果使用的是 HTTPS 方式,可能需要輸入 Gitee 的用戶名和密碼或者使用個人訪問令牌。
- 如果是 SSH 方式,則需要生成 SSH 密鑰對并將公鑰添加到 Gitee 賬戶中。
1.問題
git add .報錯warning: adding embedded git repository: carreport hint: You‘ve added another git…
原因:
使用 git add .
時,出現上述錯誤。是因為在當前 git
倉庫中同時包含有另一個git
倉庫。如當前倉庫目錄下的子文件夾內又是一個倉庫。
解決:
刪除子文件夾的.git文件, 重新add commit push
2.出錯:
! [rejected] master -> master (fetch first) error: failed to push some refs to ' 。。。'
出現這個問題是因為github中的README.md文件不在本地代碼目錄中,可以通過如下命令進行代碼合并
git pull --rebase origin master
十二、項目總結
項目的擴展
1. 建立整站搜索
- 我們搜索的內容是在boost庫下的doc目錄下的html文檔,你可以將這個庫建立搜索,也可以將所有的版本,但是成本是很高的,對單個版本的整站搜索還是可以完成的,取決于你服務器的配置。
2. 設計一個在線更新的方案,信號,爬蟲,完成整個服務器的設計
- 我們在獲取數據源的時候,是我們手動下載的,你可以學習一下爬蟲,寫個簡單的爬蟲程序。采用信號的方式去定期的爬取。
3. 不使用組件,而是自己設計一下對應的各種方案
- 我們在編寫http_server的時候,是使用的組件,你可以自己設計一個簡單的;
4. 在我們的搜索引擎中,添加競價排名
- 我們在給用戶反饋是,提供的是json串,顯示到網頁上,有title、content和url;就可以在構建json串時,你加上你的博客鏈接(將博客權重變高了,就能夠顯示在第一個)
- 熱次統計,智能顯示搜索關鍵詞(字典樹,優先級隊列)
6. 設置登陸注冊,引入對mysql的使用
以上就是對于C++項目–基于Boost庫的搜索引擎 的理解,完結撒花~
后續還會繼續更新一些項目 ,有興趣的小伙伴歡迎大家訂閱【項目】專欄~