Try   HackMD

網路基礎概論 ─ 重點整理

為什麼需要通訊協定(protocol)?

請先想像一下,不同國家的人一起坐在一場圓桌會議上,有些人說日文、有些人說英文、有些人說中文,大家雞同鴨講,無法順利溝通。這時候就可以使用一個語言,是大家都會的語言,這樣的語言就像是 protocol。

Protocol 就像是是一個格式化的標準,讓不同規格、不同語言、不同系統的系統或是平台,都能透過這樣的標準,進行資訊的流通及處理。

舉例來說,要瀏覽網站的話,要遵守網站的通訊協定,叫 http ( HyperText Transfer Protocol );而要傳輸檔案,則要遵循傳輸檔案用的 FTP ( File Transfer Protocol )。

HTTP ( 超文本傳輸協定 )

概要

了解網站前,先要知道網站的資訊是如何傳送的。Client 端( ex:是瀏覽器)發送HTTP Request (包含 head 跟 body)請求給 Server 端,Server 端收到後再回傳 Response 回應(包含 head 跟 body)給 Client 端。詳請可看此。

如何詳細看到資訊傳送的流程

  1. 按 F12 打開開發者工具。
  2. 點擊 Network 標籤,即可看到所有瀏覽器發送的所有 Request 跟 Response ( html 標籤)。
  3. Request 跟 Response 都要按照既定模式才可以傳送成功。

DNS Server ( Domain Name System )

將 Domain Name 網域名稱(www.google.com)翻譯成 IP 位址。

在 terminal 上可以使用 nslookup 的指令,後面加上欲查詢 IP 位址的網域名稱,即可查詢 IP 位址。如果查詢結果是 no response from server 時,可以在 Terminal 上使用 netsh winsock reset,再重啟電腦一次,即可順利使用 nslookup 指令。

瀏覽器只是幫助我們協作的程式

瀏覽器是協助我們發送 request ,接收 response 的程式。透過 node.js 的 library ─ request ( Simplified HTTP client ),也可以模擬瀏覽器發送 request,步驟如下:

  1. 先在 terminal cd 到專案資料夾,並輸入 npm install request指令。
  2. 下載後先新增一個 js 檔案,並在檔案內貼上官網提供的程式碼。這個動作是為了實作發送 request 。
    當中的網域名稱可自由更換到自己欲實作的網頁網域。
const request = require('request'); request('http://www.google.com', function (error, response, body) { console.error('error:', error); // Print the error if one occurred console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received console.log('body:', body); // Print the HTML for the Google homepage. });
  1. 執行node index.js > index.html,將 console.log 出來的 response 寫入 index.html 內。
  2. 執行 index.html(打開此檔案),即可順利看到最後結果。

header 跟 body

request 跟 response 內都有包含 header 跟body。 header 寫的是一些額外資訊, body 寫的是主要內容。

HTTP 請求方法

詳細更多可以參考 MDN

get

索取並顯示資訊。如顯示圖片或 html 及 css。

透過 dev tool 內可以看到 request URL。複製此 URL 並另開一個分頁去執行,所看到的畫面,就是 response。而這個背後程式所做的事情,就是瀏覽器發送了一個 get request 到這個 URL,並接收到了 response。

post

提交資料並請求伺服器處理。如輸入帳號密碼。

特別注意的是,get request 沒有 body, post 有。

put

更改並取代原先資料。

假設今天原訂單是雞腿飯 3,排骨飯 5,put 雞腿飯 2,那這樣最後的訂單會是雞腿飯 2,連排骨飯 5 都會不見。

patch

更改指定資料。

假設今天原訂單是雞腿飯 3,排骨飯 5,patch 雞腿飯 2,那這樣最後的訂單會是雞腿飯 2,排骨飯 5,排骨飯 5 會被保留下來。

delete

刪除資料。

option

回傳該 server 可支援的溝通方式。

HTTP 狀態碼 Status Code

  1. 1XX ─ 1 開頭的狀態碼:你等等 (client 端需要做一些處理)
  2. 2XX ─ 2 開頭的狀態碼:成功
    200 OK 就是完全順利成功
    204 No Content 就是成功但沒有回傳任何訊息(使用 delete 時)。
  3. 3XX ─ 3 開頭的狀態碼:去其他地方(重新導向)
    301 Moved Permanently :就是這個網域永久的被移到其他位置,下次再度使用 get 時,會直接導向另一個網域。
    302 Found(Moved Temporarily):是暫時被移到其他位置,下次使用 get 時,一樣會導到此網域。
    304 Not Modified:東西跟之前長一樣,可以從快取拿就好。
  4. 4XX ─ 4 開頭的狀態碼:你慘了(client 端錯誤)
    400 Bad Request:請求語法錯誤、或資源太大…等等。
    401 Unauthorized:未認證,可能需要登入。
    403 Forbidden:沒有權限。
    404 Not Found:找不到資源。
    408 Request Timeout : 請求超時。可再隨時提交一次請求且無須修改。
  5. 5XX ─ 5 開頭的狀態碼:我慘了(server 端錯誤)
    500 Internal Server Error:通用的伺服器出錯。(搶票時常見)
    503 Service Unavailable:伺服器暫時無法處理。

更多請看維基百科,想看有趣版可以點此

實作 server 端

透過 node.js 內的 library ─ http 實作 server 端。

  1. 新增一個 server.js 檔案,並輸入以下內容。
let http = require('http'); // 引用 library: http let server = http.createServer(handleRequest); function handleRequest(req, res) { console.log('request url: ', req.url); if (req.url === '/') { res.writeHead(200, { // 更改 response header 'info': 'index' }) res.write('content'); // 更改 response body res.end(); return; } if (req.url === '/redirect') { res.writeHead(301, { // 'Location': '/category' // 轉址到 /category 'Location': 'https://google.com' // 轉址到 google.com }); res.end(); return; } if (req.url === '/hello') { res.writeHead(200, { 'info':('hello') }) res.write('hello') res.end() return; } res.write(404); res.end(); return; } server.listen(5000); // 監聽 5000 這個 port
  1. 執行node server.js 指令(執行 server)
  2. server 會不斷的執行直到按下 Ctrl + c

驗證是否可以傳到 client 端:

  1. 打開瀏覽器的新分頁,並輸入 http://localhost:5000,應該會看到畫面上出現 content。
  2. 輸入 http://localhost:5000/hello ,畫面會出現 hello。
  3. 輸入 http://localhost:5000/redirect ,畫面會跳轉到 www.google.com
  4. 隨意輸入 http://localhost:5000/djioejfo ,畫面會出現無法連上這個網站(404 not found)。

TCP / IP

TCP / IP 是電腦網路通訊協定,分成四個層級( 應用層、傳送層、網路層、鏈結層 )。更詳細可參考鳥哥的私房菜

圖片來源:鳥哥的私房菜

從這個協定當中可以看到 HTTP 協定〈應用層〉建立在 TCP 協定〈傳送層〉上,而 TCP 協定又建立在 IP 位址〈網路層〉上。

IP 地址( Internet Protocol )

IP 全名為 Internet Protocol,中文為「 網際網路協定 」。

協定也有分版本,分別是 IPv4(Internet Protocol version 4) 跟 IPv6(Internet Protocol version 6)。IPv6 可用的地址更多,解決主要 IPV4 不夠用的問題,兩者的地址也完全不同。更多差異請看這

IP 也有分成固定 IP、虛擬 IP、浮動 IP

  1. 固定 IP:每一台電腦都有一個 IP 位置(理想狀態下),這 IP 位置是固定的,費用也較高。而基本上伺服器都跟公司企業是用固定 IP,這樣才能保證使用者可以連上伺服器,不會突然找不到。
  2. 虛擬 IP:大公司內常常會需要因為資安因素,以連上同一個類似數據機的電腦設定虛擬 IP,避免將資料帶離公司,或者是防止駭客入侵。這些虛擬 IP 只有在內網才可以互相連接,對外一律都是同一個 IP 位置(有可能是固定 IP 或浮動 IP)。詳見下圖。


圖片來源:Huli 課程內容

這樣改變位址的數據機我們稱為 NAT,底下會在更仔細的討論 。

  1. 浮動 IP:許多一般及家用戶都是浮動 IP,IP 位址會隨著每次連線而改變,好處是電信公司不需要直接給一個固定 IP,也因為浮動的 IP 位址不易受到駭客攻擊。

想要查詢自己對外的 IP 位址,可以使用「 myip 」作為關鍵字,許多網頁都有提供這樣的服務使用。

NAT

NAT,Network Address Translation,網路位址轉譯。

剛開始是為了解決 IPv4 位址不夠的的問題,而想出的一個過渡期解決辦法。目前主要的用途是有三:

  1. 封包偽裝(IP masquerade)
    • 改變私有 IP(內部 IP)改變成真實 IP(外部 IP)再傳送到網際網路。也就是我們前面提到的功能。
    • 讓其他人不知道內部到底有幾個伺服器及 IP 分配情形而增加網路安全性。
  2. 封包過濾
    • 可以攔截網際網路進入私有網路的封包,阻擋怪客(Cracker)的攻擊。
    • 因上述,NAT 是最重要的防火牆成員,也是一種「封包過濾器(Packet filter)。
  3. 負載平衡(Load Balance)
    • 通常使用在 web server,一般網頁可能同時會有很多人連線到伺服器,單一台主機(伺服器)沒有辦法負荷這麼多的連線請求,這時候就可以透過前面說的封包偽裝的特性,更改 IP 位址後發送到不同的主機,藉此減輕單一主機的負擔,同時增加網路安全性。

備註:其實有個東西叫做負載平衡器(Load Balancer),是一個軟體程式,主要就是在負責 load balance,詳細可參考 文章一文章二

資料來源

Port 連接埠( 端口 )

為了區分並接收同個電腦( IP 位址 )的不同服務,所以就有了 Port 。 在剛剛的實作一個 server 端所輸入的server.listen(5000);,這邊的 5000 就是一個 port。

預設的 port 有 HTTP (80)、HTTPS (443)、FTP (21)。常見的測試值為 3000、4000、4001。

TCP 與 UDP

TCP 與 UDP 都是傳送層中的兩個協議。

大部分的網路協定都是建立在 TCP 上,因為 TCP 較為可靠、會透過「三次握手」確保資料送達。

而 UDP 重點在於速度,在日常網路應用上像是視訊或是轉播時,重點在於傳送速度而不是傳送穩定,因為漏了一兩個封包其實對使用者並不會有明顯的影響。

何謂三次握手

三次握手又稱三路握手,全名為 threee-way handshake。簡單來說 client 端跟 server 端會通過三次的聯繫來確保資訊有確實傳達給對方。

圖片來源:由 原上傳者為中文維基百科的Huage.chen - Transferred from zh.wikipedia to Commons by Shizhao using CommonsHelper., CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=36967543

總結

層級內容 層級用義 紙條故事
HTTP / FTP 傳輸內容 紙條內容
TCP / UDP 傳輸方式 三次確認 / 講求速度
IP 傳輸地址 寄件人和收件人
實體層 實體傳輸 郵差

Server 伺服器

這裡的伺服器並非指的是像 Apache(Web Server)、FTP Server 這類管理伺服器的軟體。

這裡的伺服器指的是執行上述各種伺服器管理軟體的主機,伺服器管理軟體可能還包含 DNS Server、Mail Server、Database Server 等。通常具備較高的計算能力、能夠提供給一個或多個使用者使用的電腦/主機。

種類

伺服器常見有三個種類:

  1. 虛擬空間
    • 只有空間而已,沒有操控權
  2. 虛擬主機
    • 一台實體主機上面有很多虛擬主機,可以視為 cloud
    • 如果共用的實體主機壞了,可以自由且無痛遷移至另一個實體主機
    • 需和其他虛擬主機共享 CPU 、記憶體等實體主機的硬體效能。
  3. 實體主機
    • 自己有一台完整的主機
    • 主機壞了裡面的資料也會完全損毀,難以修復

管理

常見管理的方式有兩種:

  1. 遠端連線:一般較常見在 window 系統,可能會使用 Microsoft 為 Windows 10 所免費推出的遠端伺服器管理工具(Remote Server Administration Tools,RSAT),或者是其他第三方工具:像是 Team Viewer、VNC。
  2. SSH
    • 全名為 Secure Shell Protocol,安全的殼程式協定,提供類似 telnet 但更安全的遠端連線。
    • 透過 command line 介面直接連到遠端的主機下指令,通常是打 ssh + ip位址 ,連線成功後可以使用 top 確認主機效能。
    • 最常見、普遍的管理方式。

VPN

Virtual Private Network,虛擬私人網路。根據維基百科,VPN 是一種常用於連接中、大型企業或團體與團體間的私人網路的通訊方法。 它利用隧道協定(Tunneling Protocol)來達到傳送端認證、訊息保密與準確性等功能。

概念基本上就像是先連到一個主機,再由那個主機連線到其他地方。常見的應用:

  • 公司內網:先連到公司內網才能存取公司的資料。
  • 使用學校服務:同理,連到學校內部才能使用學校資料
  • 翻牆

API

全名為 Application Programming Interface,中文翻譯為「應用程式介面」。簡單來說透過 API 就是一個交換資料的工具。

API 有分很多種,這邊會提到的是基於 HTTP 協定下的 Web API (HTTP API)。

API 常見的 CRUD 功能分別指的是:新增(Create)、讀取(Read)、更新(Update)、刪除(Delete),有時候會聽到五項功能,多了一個列表(List)。

如何串接 API

方法一

有個簡單提供測試是否可以串接成功的 API 網站 ─ Regres。透過此網站下方 jQuery 內提供的 URL,放入測試程式碼內,即可實測。

const request = require('request'); request('https://reqres.in/api/users', function (error, response, body) { console.log( body); // Print the HTML for the Google homepage. });

在 URL 後方也可以加上欲加入的物件,像是https://reqres.in/api/users/2, 即可呈現 ID :2 的元素。

若是想要在 Terminal 上直接執行 node index.js 2 ,可以另外在檔案內加入const process = require('process');,並可以先用console.log測試 process 到底是甚麼。

在 terminal 上執行 node index.js 2,就會發現顯示出來的結果是。

第一行顯示的結果表示,node 位置。
第二行顯示的結果表示,index.js 位置。
第三行顯示的結果表示,2。

process.argv[2] 作為一個陣列,欲取第三個元素。並將此放在 URL 後。範例碼如下:

const request = require('request'); const process = require('process'); request( 'https://reqres.in/api/users/' + process.argv[2], function (error, response, body) { console.log(body); } );

這時候如果在 Terminal 內中執行 node index.js 2,即可順利顯示 ID 2 的內容,執行 node index.js 3,也可順利顯示 ID 3 的內容。

方法二

透過之前有提到的 request library 的 Github 網頁,點選 forms,並使用之前所提到的 reqres 將相關資料填入。

const request = require('request'); const process = require('process'); request.post( { url:"https://reqres.in/api/users", form: { name:'shelly', job: 'none' } }, function (error, response, body) { console.log(body); } );

執行 node index.js 也可成功寫入資料(這邊只是測試用,所以並不會真正寫入資料)。

實測串接 API

該如何實際執行 API 的 GET、POST、PATCH、DELETE等指令呢?可以透過之前提到的 request 官方文件,並 ctrl + F 找關鍵字「request.METHOD()」,找到如何使用的方法。

以下實作 DELETE 跟 PUT。

DELETE

依照官網在所提供的語法(request.delete),以下提供範本。

const request = require('request'); const process = require('process'); request.delete( "https://reqres.in/api/users/2", function (error, response, body) { const json = JSON.parse(body); console.log(json); } );

執行後會發現沒跑出任何 response。這時候我們可以在第六、七行之間加上console.log('body:', body),確認 body 內容。執行後發現 body 是空的。

這時候我們可以再透過印出 status code 確認狀態。透過console.log(response.statusCode)回傳的結果是 204 。表示是成功的,只是沒有內容(因為已經刪除完畢)。

PATCH

與 POST 運用的格式相同,範例如下:

const request = require('request'); const process = require('process'); request.patch( { url:'https://reqres.in/api/users', form: { Name:'Shell' } }, function (error, response, body) { console.log(body); } );

RESTful API

RESTful 是一種風格,該如何形容這個風格呢?就是一種語意化、更為嚴謹的描述 API 方式,目前也是最常見的方式。


本圖片源自 Huli 上課教材。

在最左邊的一欄當中是要做的事情,左二是欲使用的指令,取而代之咖啡色字體的是 RESTful 風格的指令,右二是非 RESTful 風格的寫法,右一是 RESTful 風格的寫法。

所以可以看到 RESTful 的寫法較為一致、也更好理解。

這篇簡報有更仔細地談甚麼是 RESTful API。

資料格式

之前我們提到的發送或是回傳的資料,都是有格式的。這些在電腦科學中,資料格式是電腦中儲存、傳輸資料的方式。

資料格式可以簡略分成三種:純文字與自定義格式、XML、JSON。

純文字與自定義格式

直接透過文字跟條件式定義格式。

XML

全名是 Extensible Markup Language,中文是可延伸性標記式語言。XML 跟 HTML 一樣,都是屬於 Markup Language 標記式語言,均透過前後的標籤來包覆住內容。這裡可參考更多 XML 入門文件。

以前傳輸資料經常使用 XML,現在多數人都使使用 JSON。

JSON

全名是 JavaScript Object Notation ,中文是 JavaScript 物件表示法。依照維基百科,是一種輕量級的資料交換語言,該語言以易於讓人閱讀的文字為基礎,用來傳輸由屬性值或者序列性的值組成的資料物件。

雖然 JSON 稱之為 JavaScript 物件表示法,但是這個資料格式被廣泛的以函式庫的方式,用在其他語言當中。

JSON 所需要的儲存空間相較 XML 少上許多,所以目前 JSON 是最普遍被使用的資料格式。

在程式判讀上 XML 相較另一個資料格式 JSON 需要花費更多時間耗費資源。更多兩個資料格式的比較,可以看 JSON與XML優缺點對比分析

實作 JSON

之前實作 API 時所回傳的格式,就是 JSON 格式。

const request = require('request'); const process = require('process'); request( "https://reqres.in/api/users/2", function (error, response, body) { console.log(body); } );

回傳的 response 如以下:

{"data":{"id":2,"email":"janet.weaver@reqres.in","first_name":"Janet","last_name":"Weaver","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"}}

這個回傳的格式,就是一個 JSON 格式的 string。這樣的 String 比較難像物件一樣直接存取物件的 key 。那我們可以透過內建函式JSON.parse()將 string 改成物件格式。

將上面的console.log(body)改成console.log(JSON.parse(body))。回傳的 response 則會變成以下:

{ data: { id: 2, email: 'janet.weaver@reqres.in', first_name: 'Janet', last_name: 'Weaver', avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg' } }

那如果是物件格式想要改成 JSON 格式的 string ,可以使用 JSON.stringify()

SOAP

SOAP 全名為 Simple Object Access Protocol ,中文是簡單物件存取協定。一種基於 XML 的交換資料協議規範。

目前已經很少人在使用,大多人都是使用 HTTP API,也就是我們上方所提到且實測的。

curl 指令

curl 指令操作簡單、在 terminal 就可以發送 request (需下載)。除了支援 HTTP request 之外,還可以上傳跟下載檔案。以下會簡單介紹幾種常用的 curl 指令,更多可以看 TechBridge 的介紹文章及另一篇文章,寫得非常淺顯易懂。

基本格式

curl [options] [URL..]

GET

curl [URL]

  • 發一個 GET 的 Request 到這個網址,執行後會收到 Response。
  • 可以組合其他指令,像是使用 curl [URL] > index.html。就下載此 response 到 index.html 裡。

取得 response 的 header

  1. curl -I [URL]
  2. curl -- head [URL]

這邊有一篇文章非常簡單的說明了如何使用 curl 做 http 的測試。

其他常用的 Command line 指令

Ping

ping [Url]

測試後方放入的網址是否可順利連結上。

telnet

telnet [URL] [Port]

查詢是否可以連接到指定的 port。

剛開始無法使用,參考了這篇文章 ,解決了第一步無法使用。

經由 telnet 連到 IP 位址的 port 後,可以輸入「get/」就會取得 GET request 的資料。其它指令也是可以直接操作。

補充現狀:剛先透過 nslookup 查詢 google.com 的 IP 位址,再透過 telnet 查詢 port。但目前還是似乎無法使用 telnet 的指令,可使用的指令是 telnet ptt.cc

實際串接 TWITCH API 所遇到的問題

基本上按照串接 TWITCH API 的教學文件即可,但我還是遇到非常多的問題。這邊紀錄幾個非常耗費我耐心與腦力的問題,避免之後再犯:

  1. STEP 1 :註冊完 Twitch 帳戶後在這邊註冊應用程式,重新導向網址後還需要加上 port。
  2. STEP 2 :在 terminal 輸入文件上的 sample code 時,要注意的地方有很多。
  • curl: (6) Could not resolve host:curl: (1) Protocol "'https" not supported or disabled in libcurl的問題解法:裡面的單引號('')全數都要換成雙引號("")。
  • 文件內 sample code 的斜槓是讓你換行,但 cmder 無法同時執行兩行,需要手動把 / 刪除。
  • Client-ID冒號後不可以有空格。

參考資料:單引號換雙引號

實做 API

可參考保哥的影片

tags: HTTP RequestAPI