国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議

這篇具有很好參考價(jià)值的文章主要介紹了Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

基于阻塞隊(duì)列生產(chǎn)者消費(fèi)者模型線程池的多線程Web服務(wù)器

代碼地址:WebServer_GitHub_Addr

README

摘要

本實(shí)驗(yàn)通過(guò)C++語(yǔ)言,實(shí)現(xiàn)了一個(gè)基于阻塞隊(duì)列線程池的多線程Web服務(wù)器。該服務(wù)器支持通過(guò)http協(xié)議發(fā)送報(bào)文,跨主機(jī)抓取服務(wù)器上特定資源。與此同時(shí),該Web服務(wù)器后臺(tái)通過(guò)C++語(yǔ)言,通過(guò)原生系統(tǒng)線程調(diào)用pthread.h,實(shí)現(xiàn)了一個(gè)基于阻塞隊(duì)列的線程池,該線程池支持手動(dòng)設(shè)置線程池中線程數(shù)量。與此同時(shí),該線程池通過(guò)維護(hù)任務(wù)隊(duì)列,每次Web服務(wù)器獲取請(qǐng)求時(shí),后臺(tái)服務(wù)器就會(huì)將特定請(qǐng)求對(duì)應(yīng)的特定任務(wù)加載到線程池中,等待線程池中線程的調(diào)用。由于在本項(xiàng)目中,每次的遠(yuǎn)端抓取都是短鏈接,因此在理論上,該Web服務(wù)器可以接收無(wú)數(shù)個(gè)請(qǐng)求。

按照實(shí)驗(yàn)要求,本W(wǎng)eb服務(wù)器可以接受并解析 HTTP 請(qǐng)求,然后從服務(wù)器的文件系統(tǒng)中讀取被 HTTP 請(qǐng)求的文件,并根據(jù)該文件是否存在而向客戶端發(fā)送正確的響應(yīng)消息。

在服務(wù)端終端中,服務(wù)器能夠按照不同等級(jí)打印日志信息,并且在收到請(qǐng)求時(shí)打印報(bào)文信息。

執(zhí)行效果

定義代碼下./wwwroot目錄為服務(wù)器資源的根目錄,定義./wwwroot/index.html為服務(wù)器主頁(yè)。

  • 當(dāng)客戶端請(qǐng)求資源存在時(shí),服務(wù)器將返回對(duì)應(yīng)資源。
  • 當(dāng)客戶端請(qǐng)求路徑是/根目錄時(shí),服務(wù)器默認(rèn)返回主頁(yè)。
  • 當(dāng)客戶端申請(qǐng)路徑不存在時(shí),服務(wù)端返回./wwwroot/error/404.html作為返回資源。

服務(wù)器開機(jī)
Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議
瀏覽器遠(yuǎn)端連接

服務(wù)器是騰訊云的遠(yuǎn)端服務(wù)器,瀏覽器是本地瀏覽器。這是一個(gè)跨局域網(wǎng)的跨主機(jī)測(cè)試。
Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議
客戶端申請(qǐng)不存在的資源
Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議
客戶端發(fā)送請(qǐng)求時(shí),后臺(tái)服務(wù)器所獲取到的報(bào)文
Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議
多個(gè)客戶端發(fā)起請(qǐng)求

該服務(wù)器理論上支持無(wú)數(shù)個(gè)短鏈接進(jìn)行請(qǐng)求
Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議

環(huán)境準(zhǔn)備

系統(tǒng): Linux version 3.10.0-1160.11.1.el7.x86_64

編譯器版本: gcc version 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)

資源文件/源文件準(zhǔn)備目錄結(jié)構(gòu)
Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議

  • MyThreadPool目錄維護(hù)手寫的線程池源代碼
  • ./wwwroot/目錄維護(hù)服務(wù)器資源,其中./wwwroot/是客戶端訪問(wèn)的根目錄
  • 其他文件均為HttpServer源代碼

RUN

服務(wù)默認(rèn)端口號(hào):8080

tips: 如果運(yùn)行過(guò)程中二進(jìn)制程序HttpServer沒(méi)有可執(zhí)行權(quán)限 chmod 755 HttpServer

生成可執(zhí)行:make clean;make

啟動(dòng)服務(wù)器:./HttpServer 8080

在瀏覽器中輸入:ip:port即可訪問(wèn)服務(wù)器主頁(yè)

或輸入:ip:port/index.html也可以訪問(wèn)服務(wù)器主頁(yè)

Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議
輸入不存在的路徑: 43:xxx:xxx:xxx:8080/a.txt
Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議

代碼詳解

Web服務(wù)器實(shí)現(xiàn)架構(gòu)

第一層封裝:對(duì)套接字的基本操作進(jìn)行封裝 Sock.hpp

/* 具體接口實(shí)現(xiàn)可以見源代碼 */
/* Sock.hpp */
class Sock
{
private:
    const static int gbacklog = 20; 
public:
    Sock() {}
    ~Sock() {}
  	/* 以下接口內(nèi)部調(diào)用的都是套接字的底層調(diào)用 */
    int Socket(); // 創(chuàng)建獲取套接字 -- 返回值: 監(jiān)聽套接字
    void Bind(int sock, uint16_t port, std::string ip = "0.0.0.0"); // 綁定
    void Listen(int sock); // 設(shè)置監(jiān)聽狀態(tài)
    int Accept(int listensock, std::string *ip, uint16_t *port); // 接收連接 -- 返回值: 服務(wù)套接字
    bool Connect(int sock, const std::string &server_ip, const uint16_t &server_port);
};

第二層封裝:對(duì)Http服務(wù)器的封裝 HttpServer.hpp

  • 通過(guò)維護(hù)Task類,可以把線程所需要執(zhí)行的動(dòng)作進(jìn)行封裝。通過(guò)Task類的operator()()重載,線程可以直接執(zhí)行內(nèi)部綁定好的方法。
  • 通過(guò)維護(hù)HttpServer類,當(dāng)初始化并執(zhí)行HttpServer::Start()后,啟動(dòng)線程池,Accept成功后,把相對(duì)應(yīng)的任務(wù)加載到線程池中,等待線程池中線程的調(diào)用。
#ifndef __Yufc_HttpServer
#define __Yufc_HttpServer

#include "Sock.hpp"
#include <functional>
#include "MyThreadPool/threadPool.hpp"
using func_t = std::function<void(int)>;
/* 任務(wù)類 */
struct Task
{
public:
    func_t func__;
    int sock__;
public:
    Task() {}
    Task(func_t func, int sock) : func__(func), sock__(sock) {}
    void operator()()
    {
        func__(sock__);
    }
};
/* http 服務(wù)器類 */
class HttpServer
{
private:
    int __listen_sock;
    uint16_t __port;
    Sock __sock;
    func_t __func;
    yufc_thread_pool::ThreadPool<Task> *__thread_pool = yufc_thread_pool::ThreadPool<Task>::getThreadPool();
public:
    HttpServer(const uint16_t &port, func_t func) : __port(port), __func(func);
    ~HttpServer();
    void Start();
};
#endif

Http服務(wù)器的啟動(dòng) HttpServer.cc

執(zhí)行HttpServer.cc后,main()創(chuàng)建一個(gè)服務(wù)器對(duì)象指針,并調(diào)用其中的Start()啟動(dòng)服務(wù)器。

void HandlerHttpRequest(int sockfd);函數(shù)維護(hù)每個(gè)線程所要執(zhí)行的任務(wù),其中包括以下內(nèi)容。

  • 讀取Http請(qǐng)求
  • 解析Http報(bào)文
  • 創(chuàng)建一個(gè)http響應(yīng)
  • 發(fā)送響應(yīng)至客戶端
/* 一般http都要有自己的web根目錄 */
#define ROOT "./wwwroot"
/* 如果客戶端只請(qǐng)求了一個(gè)/ ,一般返回默認(rèn)首頁(yè) */
#define HOME_PAGE "index.html"
void HandlerHttpRequest(int sockfd); // 具體實(shí)現(xiàn)可見源代碼
int main(int argc, char **argv)
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(0);
    }
    std::unique_ptr<HttpServer> httpserver(new HttpServer(atoi(argv[1]), HandlerHttpRequest));
    httpserver->Start();
    return 0;
}

ulity.hpp

提供Util類,里面提供static void cutString(const std::string &s, const std::string &sep, std::vector<std::string> *out)接口。在解析http報(bào)文時(shí),可以把傳入的字符串s按照間隔為sep的方式進(jìn)行切割,并把結(jié)果放到out里面。

Usage.hpp

提供Usage函數(shù),作為HttpServer的使用手冊(cè)。

Log.hpp

提供void logMessage(int level, const char *format, ...)函數(shù),負(fù)責(zé)打印服務(wù)器日志信息。

日志等級(jí)有:

DEBUG 調(diào)試

NORMAL 正常運(yùn)行

WARNING 警告 – 進(jìn)程繼續(xù)運(yùn)行

ERROR 非致命錯(cuò)誤 – 進(jìn)程繼續(xù)運(yùn)行

FATAL 致命錯(cuò)誤 – 進(jìn)程終止

線程池實(shí)現(xiàn)架構(gòu)

thread.hpp

  • 對(duì)原生線程進(jìn)行了簡(jiǎn)單封裝
typedef void*(*func_t_)(void*); // 函數(shù)指針
class ThreadData
{
public:
    void* __args;
    std::string __name;
};
class Thread
{
private:
    std::string __name; // 線程名字
    pthread_t __tid;    // 線程tid
    func_t_ __func;      // 線程要調(diào)用的函數(shù)
    ThreadData __tdata; // 線程數(shù)據(jù)
public:
    Thread(int num, func_t_ callback, void* args); 
  	// num-自定義的線程編號(hào) callback-線程要執(zhí)行的任務(wù) args-callback的參數(shù)
    ~Thread();
    void start();
    void join();
    std::string name(); // 返回線程名字
};

lockGuard.hpp

  • 用RAII的鎖的封裝風(fēng)格對(duì)互斥鎖進(jìn)行封裝
//封裝一個(gè)鎖
class Mutex
{
private:
    pthread_mutex_t *__pmtx;
public:
    Mutex(pthread_mutex_t *mtx)
        :__pmtx(mtx){}
    ~Mutex()
    {}
public:
    void lock() // 加鎖
    {
        pthread_mutex_lock(__pmtx);
    }
    void unlock() // 解鎖
    {
        pthread_mutex_unlock(__pmtx);
    }
};
class lockGuard
{
public:
    lockGuard(pthread_mutex_t *mtx)
        :__mtx(mtx)
    {
        __mtx.lock();
    }
    ~lockGuard()
    {
        __mtx.unlock();
    }
private:
    Mutex __mtx;
};

threadPool.hpp

線程池整體架構(gòu)
Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議

#define _DEBUG_MODE_ false
const int g_thread_num = 3; // 默認(rèn)3個(gè)線程
/* 具體實(shí)現(xiàn)可見源代碼 */
namespace yufc_thread_pool
{
    template <class T>
    class ThreadPool
    {
    private:
        std::vector<Thread *> __threads; // 線程們
        int __num; // 線程數(shù)量
        std::queue<T> __task_queue; // 任務(wù)隊(duì)列
        pthread_mutex_t __lock; // 互斥鎖
        pthread_cond_t __cond; // 條件變量
        static ThreadPool<T> *thread_ptr; // 單例模式
        static pthread_mutex_t __mutexForPool; // 保護(hù)getThreadPool對(duì)互斥鎖
    private:
        //構(gòu)造放成私有的 -- 讓線程池成為單例模式
        ThreadPool(int thread_num = g_thread_num);
        ThreadPool(const ThreadPool<T>& other) = delete;
        const ThreadPool<T>& operator=(const ThreadPool<T>& other) = delete;
    public:
        ~ThreadPool();
    public:
        static ThreadPool<T>* getThreadPool(int num = g_thread_num); // 懶漢模式--獲取線程池
        void run(); // 啟動(dòng)線程池
        void pushTask(const T &task); // 向線程池添加任務(wù)
        static void *routine(void *args); // 線程要做的事
    public:
        // 需要一批,外部成員訪問(wèn)內(nèi)部屬性的接口提供給static的routine,不然routine里面沒(méi)法加鎖
        // 下面這些接口,都是沒(méi)有加鎖的,因?yàn)槲覀冋J(rèn)為,這些函數(shù)被調(diào)用的時(shí)候,都是在安全的上下文中被調(diào)用的
        // 因?yàn)檫@些函數(shù)調(diào)用之前,已經(jīng)加鎖了,調(diào)用完,lockGuard自動(dòng)解鎖
        pthread_mutex_t *getMutex();
        void waitCond(); // 等待條件變量就緒
        bool isEmpty(); // 判斷任務(wù)隊(duì)列是否為空
        T getTask(); // 獲取一個(gè)任務(wù)
    };
    template <typename T>
    ThreadPool<T> *ThreadPool<T>::thread_ptr = nullptr;
    template <typename T>
    pthread_mutex_t ThreadPool<T>::__mutexForPool = PTHREAD_MUTEX_INITIALIZER;
    //static/全局可以這樣初始化,這把鎖是用來(lái)保護(hù)getThreadPool的
}
實(shí)現(xiàn)的一些具體細(xì)節(jié)
Http報(bào)文解析

因?yàn)槲覀兪盏降亩际莌ttp請(qǐng)求,因此,對(duì)http請(qǐng)求報(bào)文進(jìn)行解析,可以獲得客戶端信息。

以下是一段完整的http報(bào)文

GET /index.html HTTP/1.1
Host: 43.xxx.xxx.xxx:8080
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9

因?yàn)槲覀冃枰崛】蛻粜枰L問(wèn)的資源,因此我們需要提取客戶輸入的資源路徑。

http報(bào)文的第一行的第二個(gè)字段,則表示客戶端需要提取資源的路徑。

與此同時(shí),http報(bào)文每行的間隔是特殊字符串\r\n。

因此在HttpServer.cc中的HandlerHttpRequest函數(shù)中,我們首先把http報(bào)文中的每一行分出來(lái),并把第一行的第二個(gè)字符串提取出來(lái),就能得到資源的路徑。
Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議

單例模式的線程池

在本次實(shí)驗(yàn)中,我們希望一個(gè)進(jìn)程只能產(chǎn)生一個(gè)線程池,因此我們把線程池設(shè)置成單例模式。這是一個(gè)懶漢方式的單例模式。

**懶漢:**第一次需要這個(gè)對(duì)象的時(shí)候構(gòu)建該對(duì)象。

**餓漢:**在main()執(zhí)行之前構(gòu)建該對(duì)象。

  • 把構(gòu)造函數(shù)私有化

  • 通過(guò)static ThreadPool<T>* getThreadPool(int num = g_thread_num)來(lái)獲取線程池

    在這個(gè)接口中,如果執(zhí)行流是第一次執(zhí)行該函數(shù),則該函數(shù)會(huì)構(gòu)造一個(gè)線程池對(duì)象并返回它的指針。如果執(zhí)行流不是第一次執(zhí)行該函數(shù),則該函數(shù)則會(huì)返回this,即自己。

另外,為了保證線程池運(yùn)行時(shí)的線程安全,在線程池中多個(gè)操作中添加了互斥鎖對(duì)操作進(jìn)行保護(hù)。

實(shí)驗(yàn)結(jié)果分析和思考

改進(jìn)部分:

  • 實(shí)現(xiàn)多執(zhí)行流,支持多個(gè)客戶端進(jìn)行連接。
  • 封裝線程池,使得服務(wù)器可以并行地對(duì)http請(qǐng)求進(jìn)行響應(yīng)

不足部分:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-466035.html

  • 給客戶端返回對(duì)報(bào)文是靜態(tài)網(wǎng)頁(yè),暫時(shí)沒(méi)有實(shí)現(xiàn)可以支持多個(gè)客戶端之間共同交互的功能
  • 回應(yīng)報(bào)文比較粗糙,可以進(jìn)一步美化。
  • 可以進(jìn)一步優(yōu)化服務(wù)器,把服務(wù)器進(jìn)程設(shè)置成守護(hù)進(jìn)程,讓它長(zhǎng)期執(zhí)行并提供服務(wù)。
  • 沒(méi)有模擬丟包的情況

到了這里,關(guān)于Web服務(wù)器實(shí)現(xiàn)|基于阻塞隊(duì)列線程池的Http服務(wù)器|線程控制|Http協(xié)議的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包