C語(yǔ)言手寫(xiě)HTTPD網(wǎng)站服務(wù)器

網(wǎng)站服務(wù)器(HTTPD)已經(jīng)有很多版本,但是大部分對(duì)初學(xué)者都非常不友好。適合初學(xué)者學(xué)習(xí)的httpd服務(wù)器,最負(fù)盛名的當(dāng)數(shù)tinyhttpd, 但是這個(gè)版本,是基于Linux系統(tǒng)的,而且配套的CGI也是使用perl語(yǔ)言寫(xiě)的,直接勸退了大部分想學(xué)后端開(kāi)發(fā)的初學(xué)者?;诖耍匾鈱?xiě)了這個(gè)小項(xiàng)目,讓只有C語(yǔ)言基礎(chǔ)的初學(xué)者,就可以直接手寫(xiě)后端服務(wù)器,快速提升C語(yǔ)言和網(wǎng)絡(luò)開(kāi)發(fā)技能。
這個(gè)項(xiàng)目是基于tinyhttpd改寫(xiě)的,解決了以下問(wèn)題:
1. 解決了tinyhttpd服務(wù)器只支持html純文本的問(wèn)題,添加了支持圖片文件和JS腳本的問(wèn)題,可以直接支持各種復(fù)雜的網(wǎng)頁(yè)。
2. 使用C語(yǔ)言實(shí)現(xiàn)了CGI功能。tinyhttpd服務(wù)器的CGI是perl腳本實(shí)現(xiàn)的,對(duì)于C/C++初學(xué)者不友好,用C語(yǔ)言實(shí)現(xiàn)CGI功能,可以更加深刻的理解動(dòng)態(tài)網(wǎng)站的實(shí)現(xiàn)原理和實(shí)現(xiàn)方法。
3. 解決和tineyhttpd服務(wù)器中文顯示的問(wèn)題,完美支持GET和POST的中文字符。
4. 本項(xiàng)目直接使用Window系統(tǒng)實(shí)現(xiàn),C/C++初學(xué)者可以零障礙掌握學(xué)習(xí)。tinyhttpd服務(wù)器是基于Linux系統(tǒng)的,而大部分初學(xué)者對(duì)Linux系統(tǒng)并不熟悉。
5. 本項(xiàng)目在最后使用內(nèi)網(wǎng)穿透,把自己的網(wǎng)站零成本的分享給自己的同學(xué)朋友。
項(xiàng)目效果點(diǎn)這里看本教程配套視頻
項(xiàng)目準(zhǔn)備
Windows系統(tǒng)
vs2019或者任意其它版本的vs
創(chuàng)建項(xiàng)目
創(chuàng)建項(xiàng)目?
使用任意版本的VS或者VC++,創(chuàng)建一個(gè)空項(xiàng)目。

創(chuàng)建服務(wù)器端的套接字
基于網(wǎng)絡(luò)的通信,需要先創(chuàng)建“套接字”?!疤捉幼帧边@個(gè)專(zhuān)業(yè)術(shù)語(yǔ),非常古怪,被很多人吐槽。我們不要深究它為什么叫這個(gè)名字,我們只需要了解“套接字”的作用即可。
套接字,就相當(dāng)于一個(gè)“網(wǎng)絡(luò)插座”,通過(guò)網(wǎng)絡(luò)進(jìn)行通信,就是通過(guò)這個(gè)“插座”收發(fā)信息的,相當(dāng)于一個(gè)電話(huà)機(jī)的電話(huà)線插槽。

在創(chuàng)建服務(wù)器時(shí),還必須要指定一個(gè)端口號(hào)。當(dāng)一臺(tái)服務(wù)器,同時(shí)對(duì)外提供多種服務(wù)時(shí),比如WEB服務(wù),遠(yuǎn)程登錄服務(wù)等等,就需要使用“端口號(hào)”,對(duì)不同的服務(wù)進(jìn)行區(qū)別。每個(gè)服務(wù),都有自己唯一的端口號(hào)。

? 但是,服務(wù)器端在網(wǎng)站訪問(wèn)服務(wù)之前,需要?jiǎng)?chuàng)建“套接字”。

以上代碼,只是寫(xiě)了函數(shù)接口,還沒(méi)有真正創(chuàng)建套接字。馬上做詳細(xì)的實(shí)現(xiàn)。
執(zhí)行WEB服務(wù)前的準(zhǔn)備工作
在接受瀏覽器前端的網(wǎng)頁(yè)請(qǐng)求之前,服務(wù)器端需要做一些準(zhǔn)備工作,流程如下:(詳細(xì)詳解可以參考本教程配套的分享視頻)

代碼實(shí)現(xiàn)如下:



?接收瀏覽器的WEB請(qǐng)求
我們的httpd web服務(wù)器準(zhǔn)備好以后,就可以接受來(lái)自前端瀏覽器的網(wǎng)頁(yè)訪問(wèn)請(qǐng)求了。
我們先來(lái)看一下瀏覽器訪問(wèn)網(wǎng)站的完整流程:

處理瀏覽器請(qǐng)求的框架
因?yàn)榭赡苡卸鄠€(gè)用戶(hù)同時(shí)發(fā)起請(qǐng)求,為了更快的處理網(wǎng)頁(yè)請(qǐng)求,這里使用多線程技術(shù)。流程如下:

?服務(wù)端收到瀏覽器的請(qǐng)求后,accept函數(shù)會(huì)返回一個(gè)“客戶(hù)端套接字”,這個(gè)套接字對(duì)應(yīng)于這個(gè)瀏覽器客戶(hù)端。以后服務(wù)器就通過(guò)這個(gè)“客戶(hù)端套接字”和對(duì)應(yīng)的瀏覽器通信。此時(shí),服務(wù)器端有兩種套接字:
服務(wù)器端套接字:用來(lái)等待新的瀏覽器客戶(hù)端的發(fā)起請(qǐng)求,收到請(qǐng)求后,返回一個(gè)客戶(hù)端套接字。
客戶(hù)端套接字:用來(lái)和對(duì)應(yīng)的瀏覽器客戶(hù)端通信。每個(gè)瀏覽器客戶(hù)端連接到服務(wù)器后,都有一個(gè)對(duì)應(yīng)的客戶(hù)端套接字。
具體代碼實(shí)現(xiàn)如下:

?處理瀏覽器的請(qǐng)求
在新線程中,單獨(dú)處理對(duì)應(yīng)瀏覽器客戶(hù)端的請(qǐng)求。
GET請(qǐng)求報(bào)文的格式
瀏覽器發(fā)起新的訪問(wèn)時(shí),將向服務(wù)器端發(fā)送一個(gè)請(qǐng)求報(bào)文。例如,在瀏覽器地址輸入 127.0.0.1:8000 回車(chē)后,服務(wù)器端收到的完整報(bào)文如下:

請(qǐng)求報(bào)文由4四個(gè)部分組成:請(qǐng)求行、請(qǐng)求頭部行、空行、請(qǐng)求數(shù)據(jù)。具體格式如下:

第一行報(bào)文詳細(xì)說(shuō)明?

響應(yīng)報(bào)文的格式
服務(wù)器發(fā)送數(shù)據(jù)給瀏覽器時(shí),發(fā)送的響應(yīng)報(bào)文,由4個(gè)部分組成:
?狀態(tài)行、消息頭部、空行和響應(yīng)正文。格式如下:

常用的關(guān)鍵字有:

POST請(qǐng)求報(bào)文的格式?
瀏覽器發(fā)送的POST報(bào)文的格式,和GET報(bào)文格式其實(shí)是一致的,只是多了最后一部分內(nèi)容“請(qǐng)求數(shù)據(jù)”,實(shí)例如下:

最后一行“color=red”就是網(wǎng)頁(yè)提交的數(shù)據(jù)。?
報(bào)文的解析(每行代碼的詳細(xì)說(shuō)明,可參考本教程的分享視頻)





發(fā)送錯(cuò)誤請(qǐng)求的響應(yīng)包
發(fā)送501未實(shí)現(xiàn)服務(wù)的響應(yīng)包
HTTP 狀態(tài)碼分為 5 類(lèi),如下所示:
1xx 信息
2xx 成功
3xx 重定向
4xx 客戶(hù)端錯(cuò)誤
?404 表示服務(wù)器找不到瀏覽器請(qǐng)求的資源(例如某個(gè)圖片或某個(gè)文件)。
5xx 服務(wù)器錯(cuò)誤
501 表示服務(wù)器現(xiàn)在還不能滿(mǎn)足客戶(hù)端請(qǐng)求的某個(gè)功能。
代碼如下:

注意代碼中的Server關(guān)鍵字,表示服務(wù)器端的軟件名稱(chēng)和它的版本號(hào)。
發(fā)送404資源不存在的響應(yīng)包
代碼如下:
