91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么用C寫一個web服務器之基礎功能

發布時間:2021-05-27 10:26:47 來源:億速云 閱讀:127 作者:小新 欄目:開發技術

這篇文章給大家分享的是有關怎么用C寫一個web服務器之基礎功能的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

服務器架構

目標架構

以 nginx 的思想來考慮本服務器架構,初步考慮如下圖:

怎么用C寫一個web服務器之基礎功能

當然 php 進程也可以替換為其他的腳本語言,可以更改源碼中的 command 變量實現。

服務器有一個 master 進程,其有多個子進程為 worker 進程,master 進程受理客戶端的請求,然后分發給 worker 進程,worker 進程處理 http 頭信息后將參數傳遞給 php 進程處理后,將結果返回到上層,再響應給客戶端。

也考慮過使用 php-fpm 的 worker 進程池方式,那樣的話 php-fpm 進程也要仿寫了,目前還不熟悉其內部構造,如果可以簡單化,自然向其靠攏。目前對 PHP 的 SAPI 接口不熟,了解一下再考慮。

當前狀態

當前狀態的服務器還極其簡單,總結下來有以下地方待優化:

  • 當前還是單進程,需要改成多進程,最終為 worker 進程池方式;

  • 優化 socket IO 模型,考慮 epoll、事件驅動方式;

  • 只支持 HTTP GET 請求方法,未進行太多的異常處理來定義 http 狀態碼;

  • 與 php 進程的交互方式,考慮如 nginx 使用 unix domain socket 方式。

  • 協議目前只考慮了 http,后續會考慮一些基于 TCP 的協議;

雖然簡單,但服務器已經有基本的功能了:

它監聽本地地址的 8080 端口,將接收到的 http 頭中的 path 信息提出出來交給 php 進程,php 進程將參數信息處理后返回給服務器,服務器拼裝 http 響應信息再將結果返回給客戶端。

下面介紹各個功能的實現:

功能實現

socket系列方法

在介紹函數之間先用一張圖來介紹一次 http 請求中客戶端與服務器之間的交互:

怎么用C寫一個web服務器之基礎功能

如圖:服務器創建要進行:

1.調用 socket() 創建一個連接;int socket(int domain, int type, int protocol);

2.調用 bind() 給套接字命名,綁定端口;int bind( int socket, const struct sockaddr *address, size_t address_len);

3.調用 listen() 監聽此套接字;int listen(int socket, int backlog);

4.調用 accept() 接受客戶端的連接;int accept(int socket, struct sockaddr *address, size_t *address_len);

5.調用 recv() 接收客戶端的信息;int recv(int s, void *buf, int len, unsigned int flags);

6.調用 send() 將響應信息發送給客戶端;int send(int s, const void * msg, int len, unsigned int falgs);

socket 間的接收和發送信息在 C 中有幾個系列:write() / read() 、send() / recv() 、sendto() / recvfrom()、 sendmsg() / recvmsg(),可以自行選用。

另外函數參數釋義和要點,都被我注釋在代碼中了,感興趣的可以拉下來看一下,這些在網上也多有介紹,這里不再贅述。

服務器與 PHP cli 交互

然后是 C 進程和 php 進程的交互,考慮到簡單易用,目前在 C 進程中直接執行 php 腳本:

一開始使用 system() 函數: int system(const char *command);

system 函數會 fork 一個子進程,在子進程中以 cli 方式執行 php 腳本,并將錯誤碼或返回值返回。由于其結果類型不可控,編譯時會報一個 warning。而且它將結果返回給父進程時,還會在標準輸出中打印結果,在服務器執行時會拋出異常。

于是找到了另一個方法 popen, FILE * popen(const char * command, const char * type);:

popen 同樣會 fork 一個子進程來執行 command ,然后建立管道連到子進程的標準輸出設備或標準輸入設備,然后返回一個文件指針。隨后進程便可利用此文件指針來讀取子進程的輸出設備或是寫入到子進程的標準輸入設備中。

其 type 參數便是控制連接到子進程的標準輸入還是標準輸出。我們想要子進程的標準輸出,于是傳入 type參數為 字符 “r” (read)。同理,如果想寫入子進程標準輸入的話,可以傳值 “w”(write)。

另外在接收緩沖區內容的時候也出現了一點小意外:由于使用的 fgets() 方法會以換行符\n為一段的結尾,在接收 php 進程輸出時遇到換行會結束,這里使用了一個中間字符串數組line來接收每一行的信息,將每一行的信息拼裝到結果中。

代碼如下:

char * execPHP(char *args){
    // 這里不能用變長數組,需要給command留下足夠長的空間,以存儲args參數,不然拼接參數時會棧溢出
    char command[BUFF_SIZE] = "php /Users/mfhj-dz-001-441/CLionProjects/cproject/tinyServer/index.php ";
    FILE *fp;
    static char buff[BUFF_SIZE]; // 聲明靜態變量以返回變量指針地址
    char line[BUFF_SIZE];
    strcat(command, args);
    memset(buff, 0, BUFF_SIZE); // 靜態變量會一直保留,這里初始化一下
    if((fp = popen(command, "r")) == NULL){
        strcpy(buff, "服務器內部錯誤");
    }else{
        // fgets會在獲取到換行時停止,這里將每一行拼接起來
        while (fgets(line, BUFF_SIZE, fp) != NULL){
        strcat(buff, line);
        };
    }

    return buff;
}

報文數據處理

socket 處于應用層和傳輸層之間的虛擬層,由于設置服務器 socket 協議類型為 TCP,那么 TCP 的握手揮手、數據讀取等步驟對于我們都是透明的。我們拿到的數據即 HTTP 報文,關于 HTTP 報文結構和其字段解釋的文章非常多,這里也不再多提。

首先使用 C 的 strtok() 方法,獲取到 HTTP 頭的第一行,獲取到其 http 方法和 path 信息,將這些信息處理后,再使用 sprintf() 方法拼合 HTTP 響應報文,主要替換了 響應內容長度和響應內容。

感謝各位的閱讀!關于“怎么用C寫一個web服務器之基礎功能”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

邵阳市| 谷城县| 盐池县| 雅江县| 安徽省| 玉山县| 万荣县| 奉贤区| 库尔勒市| 景洪市| 鱼台县| 隆昌县| 涡阳县| 浮山县| 德昌县| 平泉县| 永胜县| 阿拉善盟| 油尖旺区| 丽水市| 永兴县| 西和县| 宜春市| 贵南县| 静海县| 柳河县| 吉首市| 南岸区| 留坝县| 纳雍县| 华坪县| 封开县| 连江县| 浑源县| 特克斯县| 松桃| 榆中县| 白朗县| 惠来县| 射阳县| 星座|