動手用c寫乙個HTTP伺服器

2021-09-16 20:14:22 字數 2537 閱讀 1623

原始碼位址

看到像這個給tinyhttpd寫readme的倉庫都有1k star的時候,我真的好氣?,所以我也寫乙個用c寫http靜態檔案伺服器的教程,而且效能更好。

c socket程式設計面向的是傳輸層。我們在這一層上來收發http報文。

http請求報文格式如下:

由於我們是靜態檔案伺服器,所以有效的請求報文是 get url 的格式。我們只要解析這個url,然後傳送對應的檔案就ok了。這個是基本的思路。

我仿照unp中對函式進行包裝的方式。對基礎函式進行包裝,在**中只使用包裝過的函式。

unix函式大多會將函式的呼叫狀態作為返回值。如socket函式,如果返回值小於零,則是呼叫出錯,這種情況我們直接結束程序並報錯。

伺服器啟動並監聽的流程是這樣的:首先呼叫socket()建立乙個服務端的套接字,然後使用bind()將套接字繫結在乙個指定的埠上。呼叫listen()將套接字從closed狀態轉換到listen狀態。而accept會返回已連線佇列的對頭。我們對accept返回的描述符的讀寫就是對客戶端的收發操作。

這篇教程選用的併發模型是執行緒池,每個執行緒分別accept的形式。

reqhandler這個struct存放的是對客戶端描述符的處理函式和在pthread_t陣列的下標。下標用於後面的pthread_create

主程序在建立完執行緒後任務就完成了,所以它一直阻塞等待就好。

tptr = calloc(thread_num, sizeof(pthread_t));

// tptr是乙個pthread_t的陣列。在啟動的時候可給出執行緒池執行緒的數量,不指明則使用預設值8。

reqhandler rh;

rh.handler = accept_request;

for (int i = 0; i < nthreads; i++)

for ( ; ; )

pause(); // everything done by threads

pthread_create(&tptr[rh.index], null, &thread_main, (void *) (rh.handler));
在各個執行緒中分別accept,這個有個問題,他們不應該同時accept。所以我們在進入accept這個函式前加上互斥鎖。

pthread_mutex_lock(&mmlock);

connfd = accept(listenfd, cliaddr, &clilen);

pthread_mutex_unlock(&mmlock);

在伺服器搭起來之後,我們就可以幹正事了。accept_request這個函式解析出http的請求方法和url並作出響應。

我們知道http的每一行是以/r/n結束,那麼getline該怎麼做呢?乙個字元乙個字元地讀,並逐一判斷是否為/r/n序列的方法顯然比較慢。所以我們做乙個自己的緩衝區。預先在客戶端描述符connfd讀入多個字元。再在緩衝區裡乙個字元乙個字元地判斷。緩衝區讀完後,再讀一次connfd。這樣能大大減少讀取connfd的次數。

獲取到http的請求方法後,如果方法不是get,我們直接返回501錯誤。說明這個方法我們還沒有實現。

可能有人會對下面這種寫法感到疑惑。其實編譯器在做預處理的時候會把連著的字串合併的。所以下面這種寫法跟寫在一對雙引號裡是一樣的。

void

unimplemented(int sockfd)

然後判斷url的檔案是否存在。這裡我們多做了一步處理,如果url是以/結尾的,瀏覽器會自動給它加上index.html,所以我們也按照這個來。

我們用open的形式開啟檔案,而不是標準io的fopen。open能拿到該檔案的描述符。這在我們下一步傳輸檔案時比較方便。如果檔案不存在,直接返回404。

void

serve_file(int sockfd, const char *filepath)

else

}

向客戶端傳送檔案,還得設定好響應報文中content-type的值,告訴對方這是乙個什麼檔案。這裡我們需要一張表,根據檔案的字尾名查詢content-type。自然是使用hash表,衝突用鍊錶的形式解決。具體請看原始碼。

傳輸檔案時,就是一對read write,unix一切皆檔案的優雅就此體現。

void

send_file(int sockfd, int filefd)

}

寫乙個索引伺服器

今天把 jaxb 返回的 xml 物件 轉到了 lucene 的 document 然後仍給 index search 一把。還算順利搞定。接下來開始解決網路介面。寫乙個基於netty的索引伺服器。接收client仍過來的xml資料報。關於netty的資料除了 url 還有一部分中文的。url 大致...

自己動手開發乙個 Web 伺服器(一)

自己動手開發 web 伺服器 會分為三個部分,將介紹如何從頭開發乙個簡易 web 伺服器。我們這就開始吧。首先,到底什麼是 web 伺服器?http請求 響應 簡而言之,它是在物理伺服器上搭建的乙個網路連線伺服器 networking server 永久地等待客戶端傳送請求。當伺服器收到請求之後,它...

寫乙個http介面服務

主要要提供以下功能 1 可提供http介面通訊,實現服務端和客戶端的應答 2 通訊資料格式為json 3 可根據介面需求運算元據庫增刪改查 4 介面通訊過程日誌記錄 5 可與其他模組進行內部通訊 寫了以上內容後,開始考慮如何實現了。目前使用jdbctemplate實現對資料庫mysql的操作,但sp...