# 第三次 ###### tags: `課程` :::spoiler TODOs ## TODOs - [x] intro - [x] network layer - [ ] TCP/IP - [x] port - [ ] ipv4, ipv6 - [x] DNS - [ ] http - [x] url, query variable - [x] header, body - [x] cookies & session - [x] http method - [x] http status code - [x] RESTful API - [ ] https ::: :::info 開頭的廢話: 圖片基本上都是偷來的 內容也參差不齊,就加減看看ㄅ ::: ## 開頭 當你打開瀏覽器,輸入某個網址,然後就得到了某個“頁面”。在這之中到底發生的什麼事。接下來就讓我們花點時間簡單的暸解一下ㄅ。 首先有兩台電腦, Client 與 Host,也就是使用者的電腦與目標伺服器。 首先 user 輸入 `URL` 或按下某個超連結, Browser 會先去尋找 `Doemain name` 對應的 `IP address`。找到後便會製造出一個對應的 `HTTP` 資料遞給 OS,OS再將其包成`TCP/IP`封包(`data packet`)透過網卡送出。 被送出的封包會經過一堆驛站,每個驛站都會為此封包指路,最後來到 Host 端。在 Host 端所有事情會被反向的進行一遍,OS 拆開 `TCP/IP` 封包,拿出 `HTTP` 資料給 `web server` 或 `application server`,這些服務再根據 `HTTP` 內部的內容做出相對應的行為,像是給出對應的 `HTML`。 在 Host 回覆時,便會將 `HTML` 等東西包裝成一份 `HTTP` 資料,交給 OS..... bla bla bla 如果我們簡化一下流程就會是: Client(Browser) -> `HTTP` -> Client OS -> `TCP/IP` -> Router -> `TCP/IP` -> Host OS -> `HTTP` -> Host(`web server`) 由 Client 送出的請求我們稱為 `Request` 由 Host 送出的回應我們稱為 `Response` 如果忽略中間的過程,我們可以簡化成 Client -> request -> Host Client <- response <- Host ```javascript= router.route('url') .get((req, res)=> { // Express 這裡的 req 指的就是來自 Client 發出的 http 資料 // res 指的是自己將要送出的 http 資料 }) ``` 至於如何包裝一份 `HTTP` 資料,以及如何與 OS 溝通,這些問題 `Node.js` 都幫我們完成了,我們只要專注於如何處理/回應傳進來的 requests 就行了。 :::warning 注意1: 上面的講解十分片面且隨便,只能作為概念了解。 跟實際上的運作有所差異。 ::: :::warning 注意2: 在這裡講的“網頁”相關的協定在其他的網路應用上,所用的協定會有所差異。 譬如影音串流等 ::: > [延伸閱讀:從按下 enter 到看到頁面的完整詳細流程(超級fxcking詳細)](https://github.com/alex/what-happens-when#check-hsts-list) ## Network layers > 隨便水過去,因為我也不會 ![](https://upload.wikimedia.org/wikipedia/commons/thumb/c/c4/IP_stack_connections.svg/800px-IP_stack_connections.svg.png =x500) > 這張圖簡單的表達了封包運送的過程 一開始的網路開發者一切都自幹,而後為了讓工作能夠分工,重複的工作可以分享,讓工程師可以專注在上層的開發... 便發明了幾套將網路流程分層的方式。 當網路資料傳送時,其中一邊的電腦一層層將資料包起來,另一邊則是將資料一層一層的分析/拆開。每一層都只會看到(也只需要)看到自己那一層負責的相關資料。 網路分層越往下越接近硬體,越往上越接進應用程式。 ### OSI 7 層 ![OSI-TCP/IP-FiveLayer](https://miro.medium.com/max/522/0*f_JZX-PtepdZwOlN.png) OSI 7 層,一個常見的分類方式。但在現在其實很少會分的這麼細,基本上會以「5-Layer model」與 「TCP/IP Model」為主。 :::spoiler 小傳聞 :::warning ##### 關於 OSI 7 小傳聞 - 據說當初發明的 OSI 7 層的人在酒吧喝的爛醉,然後有人提到或許白雪公主與七個小矮人的七跟網路很搭歐,然後他們就訂出 7 層的架構了 ::: [OSI 7 的真歷史](https://www.cs.purdue.edu/homes/comer/essay.network.layers.html) [白雪公主與七個小矮人](https://www.explainxkcd.com/wiki/index.php/1417:_Seven) [5-LAYER NETWORK MODEL](https://medium.com/@karthikayanmailsamy/5-layer-network-model-made-simplified-e813da0913ba) ### TCP/IP > [wiki](https://zh.wikipedia.org/wiki/TCP/IP%E5%8D%8F%E8%AE%AE%E6%97%8F#%E5%9B%A0%E7%89%B9%E7%BD%91%E5%8D%8F%E8%AE%AE%E6%A0%88%E4%B8%AD%E7%9A%84%E5%B1%82) #### TCP > Transmission Control Protocol > 隨便水過去 - TCP - 三相交握,SYN, SYN-ACK, ACK - 會先確認雙方連線正常 - 網頁傳送通常都用這個協定 - UDP - 不做連線確認就發資料 - 通常用在網路遊戲等即時性比掉包更重要的場合 #### Port 在 TCP/UDP 內容中有所謂的 "Port" 的概念。 Port 不是硬體上的,而是軟體上的實做,總共 16-bit,從 0 到 65535 Port 用作區分不同的服務,在連線時除了主機位置還需要指定 port,對於不同的 port TCP/UDP 協定會將它 forware 到不同的服務。 > 在主機名稱後 `:` 的數字就是指定使用哪個 port > `http://some-domain-name:3000` 有些常見的系統服務會有 default 的 port譬如: - `22`: ssh - `80`: http - `443`: https port 的前 1024 號都是系統用的,需要 root 權限才能使用。 ```javascript= // Express app 中的 listen function 的第一個參數就是你的服務要開在哪個 port // 通常 3000 port 會是拿來測試的,實際上場時會使用 80 和 443 port app.listen( 3000, '0.0.0.0'); ``` > 簡單實際示範 <!-- 使用 docker nginx 開在不同 port --> #### IP Address > Internet Protocol > 重點放在 IP Address > 像是固定IP 與 virtual IP, private network - IPv6 - 新的協定,由於 Ipv4 的位置已經開始漸漸不夠發了因此而被推出 - Address 由128 bits組成 - 以每組以4位十六進制方式表示 - e.g. `2001:0db8:86a3:08d3:1319:8a2e:0370:7344` - IPv4 - 舊的協定,目前大部分的設備還是採用 IPv4 - address 由32 bits組成 - 以每組 4 bit, 十進位表使。例如: - e.g. `35.194.166.209` - 由於 IPv4 能用的 IP 有限,因此使用 fuck my life sdfavbvjbkdfbb - WAN, LAN - class - class A, B, C - reserved ip - podcast - localhost: 127.0.01 - network mask ==TODO==: ## 網路設備 > 跳過 - AP - Switch - Router - Bridge - Gateway ## DNS > Domain Name Server > 用範例水過去,細節不重要 - 一些紀錄著 Domain name <--> IP 的對應的 server - 類 UNIX 系統下可以用 `whois` 與 `nslookup` 來查詢一個網域的相關資訊 - e.g. - ```shell $whois google.com $nslookup google.com ``` - Top domain name - `.com`, `.edu`, `.tw`, `.io` .etc - example: `ncueeclass.ncu.edu.tw` - `.tw` 下的 `.edu` 下的 `.ncu` 下的 `.ncueeclass` > 簡單實際示範 <!-- 操作一下 Godadyy --> ## HTTP > Hyper Text Transport Protocol > ==重要== ### Intro 一開始{$人名}發了一篇論文關於他發明了一套讓電腦之間傳送文本的協定,包含(URL, HTTP, HTML) ... 一開始在個人電腦流行後,Brower 也一起開始被商業運用,在“瀏覽器大戰”的時候,開始出現各種 HTML, HTTP, javascript 規格。 在最後 xxxx年,開始出統一的規格 ### URL & query string/ GET parameter 使用像是檔案路徑的感覺來指定一個 Resource 的表示法 當我們想要在此 URL 上添加一些參數可以: ``` /my-blog/article?id=123 ``` 也可以多個 ``` /my-blog/article?id=48763&name=kirito ``` 這些參數被稱作 **query string** 或 **GET parameters** > 如果你去觀察 Google search 的 URL 會發現都是在 `/search` > 而後面的 query string 的 `q` 則是你的搜尋關鍵字 #### URL in Express 在 `Express` 中會在 `router` 裡做 URL 的匹配 在 Express 中提取 query string [Doc](http://expressjs.com/en/api.html#req.query) ```javascript= router.route('/my-blog/article').get((req, res)=> { // 當 URL 為: '/my-blog/article?id=48763' console.log(req.query.id); // => 48763 console.log(req.query.kirito); // => undefined }); ``` 另一種的提取方式(非原本的 query string 格式) [Doc](http://expressjs.com/en/api.html#req.params) ```javascript= router.route('/my-blog/ariticle/:id').get((req, res)=> { // 當 URL 為: '/my-blog/article/48763' console.log(req.param.id); // => 48763 }); ``` 使用 param 的方式可以確定使用名稱 ### Header/Body > 由我來組成頭部! 一份 HTTP 資料可以分為 Header 與 Body 兩部份 ![](https://i.imgur.com/SNJBRZy.png) > `Response Headers` 代表 Response 的 HTTP Header Header 裡通常放著一些設定相關的資料,像是: ``` ==TODO:== ``` Body 則是放乘載著的資料, Response Body 就會有 HTML檔之類的東西 ![](https://i.imgur.com/7kn5gA8.png) Request Body 則會有 Client 要送出的資料(像是註冊資料) 或是上傳檔案之類的 ![](https://i.imgur.com/dudcSmQ.png) #### Express Head/Body ```json= // Body { "id": "123" } ``` ```javascript= // Node.js code console.log(req.body.id); // 123 ``` > request Body 不一定要是 json 格式,只是因為 Node.js 與 json 相性高便拿 json 格式示範 ### Cookie & Session > 餅乾! 講話! 在 header 中有一個特別的 key,叫做 `cookie`。 cookie 為很多對的 `key:value` `cookie` 會由 Host 發給 Client,而Client 的 Browser 會將 `cookie` 儲存起來,在下一次對 Host 發 Request 時,這份 `cookie` 就會被加在 header 中。 > `cookie` 的用處在於辨識 假設今天我 Host 收到一個 Request 要我做一份雞排,而後 Host Response 一個 `cookie: number=c8763`。之後,如果有一個 Request 的 `cookie` 內容是 `number=c8763` 那麼我就能知道,這個 Request 是來自剛剛跟我點雞排的人。 #### Session > 一套登入相關的機制 在 Host 端儲存一個 session table,當有使用者成功“登入”後,我們 Host 便發給此使用者一個特殊的 `cookie` 稱為 `SessionID`,內容為一串難以被猜到的亂碼。 每個 `SessionID` 會定應到到 session table 中特定的空間,這個空間裡就可以紀錄此使用者ID為何。 ```javascript= (req, res)=> { // 這裡的 req.session 就是 session table // req.session.username = 'c8763' req.session.name = req.body.username; req.session.雞排 = { 切: req.body.雞排.切, 辣: req.body.雞排.辣, }; } ``` #### JWT > 反正用不到,可以不用看這一章節 > json web tokens > 這一次 ncufresh 應該不會用到這個技術,會加上來是因為某人跟我提到這個我本來也不知道的東西 相對於傳統的 session ,另一種的登入方式。 簡單來說就是在body中加入一段加密過的 token, 裡面也包含 username 等資料 這樣一來 username 等資料便是存在 user 端而不是 host 端。 (username 資料與 Signature 的加密是"綁在一起"的,因此無法任意竄改 username,詳情請見「密碼學」) Header(標頭): 用來指定 hash algorithm(預設為 HMAC SHA256) Payload(內容): 可以放一些自己要傳遞的資料 Signature(簽名): 為簽名檢查碼用,會有一個 serect string 來做一個字串簽署 - [官網](https://jwt.io/) - [5*Ruby](https://5xruby.tw/posts/what-is-jwt) - [ithome](https://ithelp.ithome.com.tw/articles/10196759) ### HTTP Methods > 方法! 每一份 HTTP request 還會有個 **Method** 屬性,用來表達此次 request 的意圖 - GET - 取得資料 - 通常**沒有Body**,額外資訊在 URL 的 query string 上(這也是為什麼 query string 也稱為 Get parameter) - POST - 對此資源更新資料 - 要更新的資料放在 Body 裡 - PUT - 更新“部分”資料 - 一樣,要更新的東西放在 Body 裡 - DELETE - 刪除資料 - HEAD - 只取得 Header - ... HTTP Method 不是強制要遵守的!你要全部用 `GET` 或更反人類的全部用 `DELETE` 也可以。 但是為了自己與同事的頭髮著想還是遵守一下這個規範吧。 [更多細節](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Methods) #### In Express 在 Express 的 Router 中對於同一個 URL 可以讓不同的 Method 導到不同的地方 ```javascript= router.route('/my-blog/article/:id') .get((req, res)=> { controller.getArticle(req.param.id); }) .put((req, res)=> { controller.updateArticle(req.body, req.param.id); }) .delete((req, res)=> { controller.deleteArticle(req.param.id); }); ``` ### Status Code > 用來表達現在的狀態(Host 給 Client) > 像是最常見的:404 Not Found! 一套使用簡短的代碼來表達一個 HTTP 請求是否完成的方法 像是: - 2xx 表達成功 - `200` OK - 3xx 重新導向 - `302` Found (像是登入後把你導到其他頁面) - 4xx Cline 端的問題 - `404` Not Found - `481` I'm a teapot cannot brew coffe - 5xx Server 端的問題 - `500` Internal server error [詳細列表](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Status) [nyan](https://http.cat/) #### Express ```javascript= req.status(404).send('Page Not Found! QQ'); ``` ### RESTful API > 不是一套標準,而是一種風格 將每一個 http request 視為一個 "動詞+名詞" 的指令 動詞的部分由 http Method 來提供,在需要更多自定義動詞的情況下需要自己訂一套固定的表達法,在 URL裡 名詞的部分為 URL,URL 內只應該表達一個“東西” 因此每一個請求都是對某個“物件”做某個“操作” ``` this=> GET /player?id=123 this=> GET /player/123 this=> DELETE /player/123 this=> GET /player/1/item/48763 this=> GET /player/1/item/48763/_COMSUME not => GET /get_player/123 not => GET /del_player?id=123 ``` [Mopcon 共筆--RESTful](https://hackmd.io/gUGu05hES1GMbdrny70iyQ?view#%E4%BB%80%E9%BA%BC%E6%98%AFREST) ### HTTPS > Secure! 跟第三方認證機構認證自己的身份後 PAIN PAIN PAAAAINNNNNN AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - ==TODO:== ## Some References > 沒事就看看吧 - [express API doc](https://expressjs.com/en/4x/api.html) - history - [http 發展歷史](https://segmentfault.com/a/1190000023312905) - [瀏覽器 大戰](https://www.mozilla.org/zh-TW/firefox/browsers/browser-history/) - [[Code Rush] --瀏覽器大戰紀錄片](https://www.youtube.com/watch?v=VoLUvE-ny1k) - [OSI layer(ithome)](https://ithelp.ithome.com.tw/articles/10000021) - [sitcon2019 共筆: 網路 / HTTP / RESTful 觀念及實做](https://hackmd.io/@SITCON/camp2019-the-net#) - [Mopcon2020 共筆: 優雅的 API 設計 - 後端工程師必修的設計模式](https://hackmd.io/gUGu05hES1GMbdrny70iyQ) - [Medium: 「筆記」- 何謂 HTTP 傳輸協定](https://medium.com/pierceshih/%E7%AD%86%E8%A8%98-%E4%BD%95%E8%AC%82-http-%E5%82%B3%E8%BC%B8%E5%8D%94%E5%AE%9A-1d9b5be3fd24) - [鳥哥的 linux 私房蔡(詳細的網路描述)](http://linux.vbird.org/linux_server/0110network_basic.php#whatisnetwork) - [DNS blog](https://medium.com/%E5%BE%8C%E7%AB%AF%E6%96%B0%E6%89%8B%E6%9D%91/%E5%9F%9F%E5%90%8D%E7%B3%BB%E7%B5%B1-dns-101-7c9fc6a1b8e6) - [優質文章列](https://ithelp.ithome.com.tw/users/20116003/ironman/2935?page=2) - 我看不懂 - [syscall & TCP/IP](https://developer.ibm.com/technologies/systems/articles/au-tcpsystemcalls/)