# http 架構 [![hackmd-github-sync-badge](https://hackmd.io/HfXJwWd1TP2et8gHkc03bQ/badge)](https://hackmd.io/HfXJwWd1TP2et8gHkc03bQ) 首先在開始撰寫之前,先來個好文推薦,我有很多觀念是透過這個網站理解的 [NotFalse 技術客](https://notfalse.net/http-series) --- 基本上說到架構就需要提到 [OSI模型](https://zh.wikipedia.org/wiki/OSI%E6%A8%A1%E5%9E%8B) 的七層網路架構,但是對於一二三層的部分我們都不介紹了,感謝前人的努力讓我們對網路的基本架構都不需要煩惱</br> 我們直接從第四層、傳輸層來對http的架構開始介紹,第四層包括 TCP、UDP、DCCP、SSL等等的 </br> 基本上以上所說的TCP協議等等的,在現代的OS系統皆有遵循以及提供system call提供調用</br> 目前來說http是建構在TCP之上的,所以希望讀者能夠有一些TCP的概念(三方交握,四方揮手)以及使用過Socket等等的 --- 剛剛說過目前http是建構在tcp上 ( 先不提gooogle想推的HTTP/3 [QUIC](https://zh.wikipedia.org/wiki/QUIC) )</br> 所以我們能夠透過撰寫 Socket 的 Tcp Server 來接 http request 觀察 http 的請求是什麼樣子 [參考文章](https://notfalse.net/39/http-message-format) ``` GET / HTTP/1.1 Host: 127.0.0.1:5678 Connection: keep-alive sec-ch-ua: "Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99" sec-ch-ua-mobile: ?0 DNT: 1 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 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.9 Sec-Fetch-Site: none Sec-Fetch-Mode: navigate Sec-Fetch-User: ?1 Sec-Fetch-Dest: document Accept-Encoding: gzip, deflate, br Accept-Language: zh-TW,zh;q=0.9,ja;q=0.8,en-US;q=0.7,en;q=0.6,zh-CN;q=0.5 ``` 我們會先得到這段訊息,這段訊息其實就是 http 的 起始行 (start-line ) 和 header </br> > 起始行 (start-line) :</br> > 就是第一行 `GET / HTTP/1.1`</br> > 他會提供 request 的 method 以及 http 的版本,假如你有在網址加上一些request-target , query像是這樣`http://127.0.0.1:5678/?id=1`那起始行會像這樣`GET /?id=1 HTTP/1.1`</br> 接下來是 header 他存者很多 request 的相關資料,比如用的瀏覽器與版本、語言等等的,也可以在前端設定傳遞一些資訊給後端 接下來可能有人覺得奇怪 http 只有 header 嗎? 那 POST 怎麼辦怎麼沒有 Body,有的只是剛剛的 method 為 GET 假如我們使用 POST 得到的結果會像這樣 ``` POST / HTTP/1.1 Host: 127.0.0.1:5678 Connection: keep-alive Content-Length: 11 DNT: 1 sec-ch-ua-mobile: ?0 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36 sec-ch-u a: "Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99" Content-Type: application/json Accept: */* Origin: chrome-extension://aejoelaoggembcahagimdiliamlcdmfm Sec-Fetch-Site: none Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Accept-Encodin g: gzip, deflate, br Accept-Language: zh-TW,zh;q=0.9,ja;q=0.8,en-US;q=0.7,en;q=0.6,zh-CN;q=0.5 Hello Word! ``` 大部分的地方都差不多,但是在Body上會有一行空白</br> > 這個是 CRLF(carriage return followed by line feed) 簡單來說他不是一般的換行(`\n`),而是比較嚴謹的換行(`\r\n`),但好像現代語言用`\n`就可以了</br> 假如未來有需要撰寫時要記得在header後面會有一行換行 --- request 的部分講完了接下來換成講 response 基本上大架構是一致的,比較不同的是起始行會長這樣`HTTP/1.1 200 OK`</br> 那個 200 就是成功連線,但是200平常比較少見(因為成功通常不會特別跟你說成功),平常比較常見的通常是404(網頁不存在)或403等等的錯誤訊息,有興趣的可以去找找 [HTTP Status Code](https://zh.wikipedia.org/wiki/HTTP%E7%8A%B6%E6%80%81%E7%A0%81)</br> 簡單來說在處理 http server 時假如要給狀態碼就是在起始行做設定 --- > 假如有人要測試</br> 可以使用 : </br> [傳request軟體](https://chrome.google.com/webstore/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm) or [Postman](https://www.postman.com/downloads/)</br> and 伺服器程式 ``` c #include<stdio.h> #include<stdlib.h> #include<sys/socket.h> #include<netinet/in.h> #include<string.h> #include<arpa/inet.h> #include<unistd.h> int main() { struct sockaddr_in server,client; int sock,readSize,addresssSize,i,csock; char buf[256]; bzero(&server,sizeof(server)); server.sin_family = PF_INET; server.sin_addr.s_addr = inet_addr("127.0.0.1"); server.sin_port = htons(5678); sock = socket(PF_INET,SOCK_STREAM,0); bind(sock,(struct sockaddr*)&server,sizeof(server)); listen(sock,5); system("lsof -i:5678"); addresssSize = sizeof(client); csock = accept(sock,(struct sockaddr*)&client,&addresssSize); while(1) { readSize = recv(csock,buf,sizeof(buf),0); buf[readSize] = '\0'; printf("%s \n",buf); } close(sock); } ```