--- title: 'Http 協定 / Https / URL' disqus: kyleAlien --- Http 協定 / Https / URL === ## OverView of Content 如有引用參考請詳註出處,感謝 :smile: [TOC] ## 指定訪問文件 - URX ? * 由於我們是訪問伺服器上的某個資源,所以我們就要指定資源,而指定資源通常幾種說法 URI、URL、URN... 最常見的就是 URL * `U` 在不同時代代表了不同意思 * 早期代表了 **`Universal` 萬用** * 標準後代表了 **`Uniform` 統一** * `R` 代表了 `Resource` 資源 * **URL、URN 其實是 URI 的一個小分支**,也就是說 URL、URN 是 URI 的子集 :::info * URL & URI 差別 | 類型 (全名) | 功能 | 其他 | | -------- | -------- | -------- | | `URL`(`Uniform Resource Location`)| 資源路徑 | URL 是一種**指向路徑** | | `URN`(`Uniform Resource Name`) | **資源標識符** | 某項 **獨一無二** 的資源,通常用於永久標識資源,並與資源的位置或存取方式無關 | | `URI`(`Uniform Resource Identifier`)| 資源實際位子 | **能直接找到資源的就是 URI** | ::: > URL 可以代表協議,以及要訪問的資源位置、設定 > > ![](https://i.imgur.com/DGD7p8H.png) ### 協定 Scheme ```shell= https ``` * **==Http 代表此 URL 使用的協定==**,常見的還有 Https、SMTP、POP3、FTP、mailto... * **Http 是超文本傳輸** 而 http++s++ 表示了以 SSL (Secure Socket Layer) 機制傳輸,該加密會導致擷取的到資料無法被一般解析,而 **SSL 機製作用於應用層到傳輸層之間(++通常在 [表達層](https://hackmd.io/AIIw1ejaSWykNrWSsMH8Mw?both#%E8%A1%A8%E7%A4%BA%E5%B1%A4) 加密++)** :::warning 封包訊息可以使用 `wireshark` & `tcpdump` 攔截到,https 也可以攔截到但是會看不懂它的訊息 ::: * Https/Http 協議之後也可以接著使用者名稱、密碼,一般格式如下 > 如果使用 Http 的話,不建議用這種方式登入 ```shell= # 協議://<使用者名稱>:<密碼>@<主機>:<Port>/<Path> https://kylePan:abcd1234@www.hello.com:1000/world ``` ### 主機地址 Host ```shell= www.google.com ``` * 從`//` 至 `/` 之間的 **www.google.com 這代表了伺服器的位址,正確來說是 FQDN (完整網域名稱),而 ==FQDN 必須藉由 DNS(網域解析) 轉換為 IP== 地址才可通訊** > www 則是指伺服器端的服務是屬於 Web 伺服器(萬維網) :::warning 上面所說道的 **伺服器** 並非指電腦,**是指一組服務程式**,http/https 就是網頁服務、ftp/ftps 指的是檔案服務,**一台電腦可以有多個服務** ::: * Linux、Windows 都可以 **透過指令 ++nslookup++ 查詢到相對應的 IP**, 一個 IP 可以有多組 DNS > ![](https://i.imgur.com/4KD72Se.png) * **TCP/IP 協定除了需要 ip 地址外,還==需要端口 (port)==**,而上面不需要 port 的原因在於,**它使用常規端口所以不用特別加上 port,用法在 https//ip ==:port==** ```htmlmixed= 以下是相同的功能 // 使用 ipv4 訪問 google https://172.217.27.132 // 使用 ipv6 訪問 google,中括弧必須 https://[2404:6800:4008:800::2004] // 使用名稱訪問 https://www.google.com ``` :::warning * **直接使用 IP 訪問實會出現 ++不安全連線++**,這是因為 **==憑證==** 的問題,**這種狀況只會出現在 https 中,因為 https 才需要憑證** ::: ### 網頁 Path - 參數 Quary-String ```shell= search?client=ubuntu&channel=fs&q=ubuntu+drawio&ie=utf-8&oe=utf-8 ``` * **從`/` 至 `?` 之間的 search,是真正的請求主體**(Get 請求) * 把 URL 的第一個斜槓`/`稱為 **根目錄 (如同 Linux 的根目錄)** * 而 **`serach` 才是主要要服務的主體** * **`?` 之後統稱為 `欄位` or `參數`**,`client=ubuntu&channel=fs&q=ubuntu+drawio&ie=utf-8&oe=utf-8`,**組跟組之間使用 ==&== 區分開來** 每一組參數在等號左方稱為參數名,右方成為參數值 (key & value),若是沒有參數則不寫任何值 * **`#` 稱為錨,也就是網頁的讀取點**,像是 #name=454 (454 開始閱讀這個網頁) ```htmlmixed= // 原參數 client=ubuntu&channel=fs&q=ubuntu+drawio&ie=utf-8&oe=utf-8 // 解析參數出 5 組 1. client=ubuntu 2. channel=fs 3. q=ubuntu+drawio 4. ie=utf-8 5. oe=utf-8 ``` ### URL 編碼 - 百分比編碼 * **保留字元** 在 URI 的規範中,定義了「保留字元」(`Reserved character`),像是 `:`、`/`、`?`、`?`、`&`、`=`、`@`、`%`... 等等 * 參數欄位的特殊符號,當參數有些特別符號時 (很常看到這些特別符號),會去搜尋 [**ASCII**](https://zh.wikipedia.org/wiki/ASCII) 碼對應,使用 %<ASCII 16進制\> 轉換成 URL > URL 中可以使用 `+` 代表空白也就代表 `%20`,而真正使用 `+` 號時請使用 `%2B` | 符號 | 轉換 | | -------- | -------- | | 空白 | %20 | | + | %2B | | < | %3C | | = | %3D | | > | %3E | | ? | %3F | * **中文字元** 針對英文可以使用 ASCII 編碼,用一個位元的 UTF-8 即可;但如果使用中文(或是非英文),那使用 ASCII 編碼就會不夠表達,這時 UTF-8 就會是 2 ~ 3 位元的大小 :::danger * **編碼並非一定要使用 UTF-8 嘛?** 只要遊覽器、伺服器端講好規範就可以,如果兩者不按照規定解析,仍會解析錯誤 > 中文字「林」,UTF-8 是 `E6、9E、97` 在 URL 編碼之下就會變為 `%E6%9E%97` > > 而如果瀏覽器使用 BIG5 則是編碼為 `%AA%4C` ::: ### Java URI - 編碼工具 * Java 也提供 URI 編碼方案(`ISO_8859_1` 是國際標準化組織 (IOS) 為 西歐語言的字元碼制定的編碼,與 `ASCII` 相容) ```java= public static void main(String[] args) { String encodeUrl = URLEncoder.encode("https://hello.world?url=http://123", StandardCharsets.ISO_8859_1); System.out.println("encode url: " + encodeUrl); String decodeUrl = URLDecoder.decode(encodeUrl, StandardCharsets.ISO_8859_1); System.out.println("decodeUrl url: " + decodeUrl); } ``` > ![image](https://hackmd.io/_uploads/rkGXneW46.png) * Java 中有一個 URI 類 (`java.net.URL`),它可以自動轉換出需要的資訊 ```java= import java.net.URL; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class Main { public static void main(String[] args) throws IOException { URL url = new URL("https://hackmd.io/?nav=overview"); System.out.println("Protocol:" + url.getProtocol()); System.out.println("Host:" + url.getHost()); System.out.println("Port:" + url.getPort()); System.out.println("Path:" + url.getPath()); InputStream is = url.openStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String str = br.readLine(); while (str != null) { System.out.println(str); str = br.readLine(); } br.close(); isr.close(); is.close(); } } ``` 1. 如果沒有設定 port 的話,java 內部實現預設為 -1 2. **URL 可直接讀取網頁 IO 內容,也就是 html 的文本** > ![](https://i.imgur.com/r3SjdFU.png) ## Http 協議 Http 通訊協定是架構在 TCP/IP 應用層之上的協定;其特性是 ^1.^ **無狀態**、^2.^ **基於請求(`Request`) / 回應(`Response`)的模型** * Http 歷史簡介 | 年分 | 版本 | 功能、特色 | | -------- | -------- | -------- | | 1991年 | HTTP 0.9 | 只有一個 GET 命令、只能回覆 HTML 超文本 | | 1996年 | HTTP 1.0 | 除了 GET 還引入 POST、HEAD 命令,每次回覆都需要包括頭訊息 | | 1997年 | HTTP 1.1 | 更加完善,目前大部分還使用中 | | 2009年 | SPDY | Google 為了改善 Http 1.1 效率研發 | | 2015年 | HTTP 2 | SPDY 協議的主要特性也在其中 | ### Http 特性 1. 典型的 **C/S 架構** (client & server) > **基於請求(`Request`) / 回應(`Response`)的模型** 2. **速度快,只需要傳送方法 & 路徑就可以得到返回的數據** 常用的方法有 `GET`、`POST`、`HEAD`...每種方法規定了客戶端 & 服務端聯繫的類型 3. **靈活,Http 允許傳輸任何類型的數據**,**傳輸的類型使用 ++Content-Type++ 標記** 4. **無連接,限制每一次的連接只處理當次的請求**,加快處理速度 5. **無狀態**,對於事務處理不會有記憶 (不會記錄過往訊息,也就**不會給伺服器添加紀錄的負擔**) :::info * 也是 **由於 Http 的特性,MVC 才發展出 `Model2` 模式**、`Session management` 會話管理機制 ::: ### Http Request - Get / Post 請求結構 * **Http 請求結構就是一個文本**,文本每一個字都是 ASCII 碼(「字元」),字串長度不固定,依照需求增加,一般而言包括 4 個部分 ```shell= # Get # 請求行 GET /example HTTP/1.1 #請求頭 Host: www.example.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:100.0) Gecko/20100101 Firefox/100.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br Connection: keep-alive Upgrade-Insecure-Requests: 1 ## ----------------------------------------------------------------------------------------- # Post # 請求行 POST /api/data HTTP/1.1 #請求頭 Host: www.example.com Content-Type: application/json Content-Length: 32 # 空行 # 請求內容 {"key1": "value1", "key2": "value2"} ``` > > ![](https://i.imgur.com/6Hwf7LS.png) 1. **請求行**:透過它來表達出通用操作模式、語意與行為定義;HTTP 也定義一套固定的操作方式,可以讓任何資源遵守,它稱之為「動詞, `verbs`」 ```htmlmixed= // 不需要<>,單純為了區隔 <Method> <Request-URI> <HTTP-Version> <CRLF> ``` | 動詞 Method | 功能 | | -------- | -------- | | **`GET`** | 請求獲取 `<Request-URI>` 的資源內容 | | **`POST`** | 請求獲取 `<Request-URI>` 的資源內容,**==並附加新的內容==,相當於資料庫中的 `insert` 行為** | | `HEAD` | 請求獲取 `<Request-URI>` 的資源的報頭 (header Message) | | `PUT` | 請求服務器,**++儲存++一個資源至服務器**,相當於資料庫中的 `update` 動作 | | `DELETE` | 請求服務器,**++刪除++一個服務器內的資源** | | `TRACE` | 請求服務器回傳收到的訊息,**常用於診斷是否由傳輸錯誤** | | `CONNECT` | Http 1.1 協議中預留給能夠將連接改為管道方式的代理服務器 | | `OPTIONS` | **查詢服務器的功能、性能** | 2. **請求頭 (`Overview`)** **它是 `Key : Value`,而且有++多行請求頭++**,並使用 `:` 區分開來;伺服器端可以依據需求取出做適當的回應… 如下 * `User-Agent` 中得知使用者使用的瀏覽器資訊 * `Accecpt-Language` 了解遊覽器可接受哪些語系的內容回應 3. **空行**(`\r\n`) 4. **請求數據、參數 (`Context`)** 在 GET 方法並不會使用到數據,因為 GET 只有要求數據,但 Get 可以攜帶參數在 URL 中,給伺服器判斷 ```shell= https://www.google.com/search?q=HelloWorld&sca_esv=582168257 ``` :::danger * 大量的數據不適合使用 Get 請求,因為 Get 有長度限制(每個遊覽器都不同) ::: **++POST 方法才會使用到++,POST 方法需要提交文本,提交給伺服器資料**… 這個資訊通常用「表單」的方式發送,這是伺服器端對於使用者的要求 ### Get、Post 選擇 - 冪等概念 * **Get、Post 選擇** * **Get 請求的特色** * Get 參數會跟隨在 URL 之上,不過由於瀏覽器 URL 長度限制,所以不適合處理大量、長度長的資料 * 如果希望保留網頁頁面,並且參數也會影響到頁面的展示,那就可以用 GET 請求,將參數保留在 URL 之上 * **Post 請求特色** * 由於 Post 不會將請求體顯示在 URL 之上,所以相對來講,較為安全 * 但 Post 行為可能不夠即時,因為它並非同步操作 > 回應可能是 201 `Created` 或是 202 `Accpeted` * **選擇請求** * **操作類型** 來決定: 如果 **不希望瀏覽器 Cache 網頁資訊,就可以用 Post** 因為遊覽器可能會依照 URL 來 Cache,而使用 Post 就可以每次都攜帶最新資料到後端處理,不會 Cache * **冪等(`idempotent`)操作** 來決定 是否冪等,就是判斷請求的操作 **是否改變伺服器的狀態**、**是否同一個操作重複多次**、**是否回傳相同結果** :::success 冪等源於數學,後來才延伸到電腦領域;它是指函數可以使用相同的參數重複執行,並每次得到相同的結果) ::: * **Get 適用於冪等操作**:因為 Get 不會改變伺服器狀態,對於 DB 來說相當於做 `select` 操作 > Get 的請求參數,它只用於告知伺服器,**必須根據請求參數回傳對應的內容**,並且每次回傳都是相同的 * **Post 則是「非」冪等操作**:對於 DB 來說相當於做 `insert` 操作(同資料可能操作多次) > **Post 發送的資料可能影響伺服器上的資料、狀態**,像是增刪改查.. 等等操作,而且同一份資料可能會得到不同結果 :::info * 相較之下,`put`、`delete` 操作則是等冪操作 因為我們發送 10 次 `PUT` 訊息會是相同的結果,但是如果我們發送 10 次 `POST` 訊息則可能得到不同的結果 ::: ### Http Response 結構 * Http 回應結構也同樣是一個文本,文本每一個字都是 ASCII 碼,字串長度不固定 ```shell= # 狀態行 HTTP/1.1 200 OK # 響應頭 Date: Sun, 13 Nov 2023 12:00:00 GMT Server: Apache/2.4.38 (Unix) Content-Type: text/html; charset=UTF-8 Content-Length: 1234 # 空行 # 響應數據 <!DOCTYPE html> <html> <head> <title>Example Page</title> </head> <body> <h1>Hello, World!</h1> <p>This is an example response.</p> </body> </html> ``` > ![](https://i.imgur.com/VLql0LU.png) 1. **狀態行** (該行狀態碼較重要) ```htmlmixed= // 不需要<>,單純為了區隔 <HTTP-Version> <State-Code> <Reason-Phrase> <CRLF> // Reason-Phrase 為狀態碼的文本描述 ``` 以下舉幾個常見的狀態碼 | State-Code | Reason-Phrase | 意義 | | -------- | -------- | -------- | | 200 | OK | 客戶端請求成功 | | 400 | Bad Request | 客戶端語法錯誤,服務器無法解讀 | | 401 | Unauthorized | 未經過授權,該狀態碼必須還 www-Authenticate 報頭域一起使用 | | 403 | Forbidden | 服務器收到請求,但拒絕提供該服務 | | 404 | Not Found | 服務器找不到請求的資源或是文件 | | 500 | Internal Server Error | 服務器內部錯誤而妨礙了請求的執行 | | 501 | Service Unavailable | 服務端維護中或是暫時超載,無法立刻響應該請求 | | 503 | Server Unavailable | 服務器當前不能處理客戶端請求,一段時間後可能會恢復正常 | 狀態碼的區間 | 區間 | 意義 | | -------- | -------- | | 100~199 | 提示訊息,收到請求,是用者可以繼續執行操作 | | 200~299 | **請求成功**,請求已被接受並處理 | | 300~399 | **重新定位**,請求者**必須**更進一步操作 | | 400~499 | **客戶端錯誤** | | 500~599 | **服務端錯誤** | 2. 響應頭 (Overview) **它是 Key : Value,而且有++多行請求頭++**,並使用 `:` 區分開來 3. 空行 4. 響應數據 (Context) ### Http Header 訊息:客戶端、伺服器端 * 以下要說明的是 HTTP 定義的內容協商(`content negotiation`)機制,稱之為 Http Header,這些 Header 選項可以超過一個以上 ```shell= header1[;q=1] [; header2...] ``` > `q=1` 是品質因數,因數越高代表越能接受的格式 * 我們將 Http Header(報頭)分為幾個大分類,分別是客戶端的請求報頭、服務器端的響應報頭、通用報頭… 等等 1. General-header **通用報頭** > 可出現在請求(客戶端)、響應(服務器)報頭 | Key | 功能 | | -------- | -------- | | `Data` | 消息產生的時間 & 日期 | | `Conntion` | 允許發送指定連結的選項,可指定是連續的、傳送完畢就關閉...等等 | | **`Cache-Control`** | **緩存策略,++該指令是單向的++ (客戶端緩存機制、服務端緩存機制可不同)** | | `cookies` | 儲存訊息 | | `Transfer-Encoding` | **告知服務器端該如何解碼** | 2. request-header 使用端專用報頭 > 通知服務器關於**客戶端的請求訊息**,**客戶端報頭大多以 `Accpet` 開頭**,它會告訴服務端它所接收的資料類型、語言、編碼… 等等設定 | Key | 功能 | | -------- | -------- | | `Host` | **請求的主機名稱**,多個域名指向同一個 IP 稱為 **虛擬主機** | | `User-Agent` (代理) | **客戶端資訊**,遊覽器類型、操作系統等等 | | `Accept` | **客戶端可處理的內容類型列表**,用於指定客戶端接收的數據類型 | | `Accept-Encoding` | **客戶端可接受的編碼類型** | | `Accept-Language` | **客戶端遊覽器所支持的語言** | | `Connection` | **客戶端 & 服務器端連接方式相關**,Ex: keep-alive 保持連線 | 3. response-header 服務端專用報頭 > 用於 **伺服器傳遞自身訊息**的響應,**伺服器端報頭大多以 `Content` 開頭**,用來指定回傳的資料格式 | Key | 功能 | | -------- | -------- | | `Location` | 重新定向,常用於要求 Client 端更換域名 | | `Server` | **與 User-Agent 相對應,代表了服務器端的規格** | entity-header 實體報頭 > 定義被傳送資源的信息,**簡單來說就是 ++描述傳送的資源++** 所用的 Header | Key | 功能 | | -------- | -------- | | `Content-Language` | 內容語言(人們用得自然語言) | | `Content-Length` | 實體主體的大小,但為`Byte` | | `Content-Location` | 重新導向 URI 的位置 | | `Content-MD5` | 用於驗證訊息主體的完整性(通常 128 bit) | | `Content-Range` | 指定實體資料的範圍 | | `Content-Type` | 內容主體的媒體類型 | | `Content-Encoding` | 內容的編碼模式,來獲得正文的解碼方式 | | **`Last-Modified`** | 該資源最後修改的日期 & 時間 | | **`Expires`** | 實體報頭給出響應過期的日期 & 時間 | **--[網頁範例](https://www.wanandroid.com/project/tree/json)--** (開啟網頁後,打開 F12 開發者模式,選擇 `Network` 就可以見到) > ![image](https://hackmd.io/_uploads/S1p68KuWA.png) ## Http 緩存機制 * 存在 http 的 **Header 頭訊息 Cache-Control** * 為了不要讓客戶端太過頻繁訪問伺服器,否則導致流量消耗過大、負擔過重,使用緩存機制 (雙方不一定都有此機制),以下有幾種常用的緩存機制 1. `Cache-control` -> ++**Max-age**++ > 為了修正 Http1.1 Expires 的漏洞 (C/S 時間不一致),Max-age 的單位為秒 2. `Cache-control` -> ++**no-cache**++ > **會先經過對比緩存**,傳緩存標誌給伺服器端,如果有需要才會請求報文,否則伺服器返回 304 代表該緩存可用 3. `Cache-control` -> ++**no-store**++ > 連對比緩存都沒有,**每次都會發送請求訊息報文** ### 緩存過程 * 下圖是第一次請求時的流程,當**返回數據時會將響應頭中的 `Cache-control` 規則存入本地的緩存**,之後每次請求都會進行比對,這樣才不會浪費資源 > ![](https://i.imgur.com/6EOtWmb.png) * **強制緩存**,每次比對都會本地緩存,比對 **==expires== (Http\1.0)、private、public、max-age、no-store (每次都請求數據)、no-cache (對比緩存)**,**當數據已過期就會再次請求伺服器,否則會繼續使用緩存資源** > ![](https://i.imgur.com/C00XCFF.png) 強制緩存中的 `cache-control` ==**使用 no-cache 緩存**== 機制,需要透過 **++對比緩存++**,**成功返回 304 代表不用更新數據** (服務端 & 客戶端都要支持) > ![](https://i.imgur.com/bmygFP1.png) ### 對比緩存 * 對比緩存一定有一個**對比的標示**,而**決定該標示是否有用在於伺服器**,**而該標示也有優先級**,該標示如下表 | 優先級 | 對比標示 | 功能 | 作用 | | - | -------- | -------- | -------- | | **低** | `Last-Modified` / `If-Modified-Since` | 伺服器會同時返回這兩個標示,當客戶端要再要求資訊時會傳出 `If-Modified-Since`,伺服器端就會比對 | **在伺服器端比對**,`Last-Modified` < `If-Modified-Since` 更新;`Last-Modified` > `If-Modified-Since` 返回 304 取用暫存 | | **高** | `Etag` / `If-None-Match` | 伺服器會返回這兩個標示,當客戶端在要求時會傳出 `If-None-Match`,讓伺服器端比對該條訊息 | **在伺服器端比對**,比對 `Etag` 是否相同,不同則需要更新,相同返回 304 | > ![](https://i.imgur.com/pEwu1Ov.png) ## Http 版本區別 ### Http 1.0 * 請求 & 響應支持頭域 (Header Message) * 響應對象以一個響應狀態行開始,並不只限於超文本 * **開始支持客戶端通過 POST 方法向伺服器提交數據,支持的請求方法有 GET、PUT、HEAD** * **頭域支持長連接 (須設定,默認是端連結)、緩存訊息** ### Http 1.1 * **頭域支持 ==keepalive 連結==** * chunked 編碼傳輸、字節範圍請求 * **請求頭域支援 Host** * 新增請求碼,OPTIONS、PUT、DELETE、TRACE、CONNECT 方法 * 緩存處理 > 基於 1.0 上加入了一些 cache 的特性,引入實體標籤,依般稱為 e-tags,新增更強大的頭域 Cache-Control 報頭 ### Http 2.0 | 名稱 | 功能 | | -------- | -------- | | **多路覆用 (二進制分貞)** | **Http 2.0 最大的特點**,不改動 Http 格式 (保留方法、狀態碼、頭域...),**==改進傳輸效能,++新增二進制分貞層++==,將信息切成更小的單元傳輸,並採用二進制格式傳輸** | | 頭部壓縮 | 當客戶端**向同一個伺服器**請求多個資源時,會有大量相似訊息、這就**需要頭部壓縮,增高傳輸效率** | | 隨時復位 | Http 1.1 的一個缺點在於,當 Http 訊息有一定長度大小數據傳輸時,不能隨時中斷它 (中斷 TCP 連接的代價太高,可能導致全文件重新傳輸),Http 2.0 的 REST_STREAM 能快速、方便的停止一個訊息傳輸 | | 服務端推流 (Server Push) | 當客戶端請一個資源後,需要用到多個資源時,無須詢問客戶端就會自動把需要的資源存入緩存 | | 優先權 & 依賴 | **每一個流都可以有優先級別,放便優先級加載** | ## Https 請求 發出 Https 請求時的處理順序是… 0. 透過 DNS 域名解析器,解析目的 IP 地址 1. 三握手建立 TCP 傳輸層連接 2. **客戶端向服務器發送請求行命令** `GET www.google.com HTTP/1.1` > 客戶端發送,請求頭訊息 3. **服務器端響應訊息** Http/1.1 200 OK > 返回響應頭訊息 4. 服務器向客戶端發送數據 5. 關閉 TCP 連線 ### Https 請求過程 * **Https 是基於 Http 新增 SSL 協議來保護傳輸訊息,比起 Http 多了一個握手的過程**,[**可參考知乎**](https://zhuanlan.zhihu.com/p/37738632)、[**BINANCE**](https://www.binance.vision/zt/security/symmetric-vs-asymmetric-encryption) | 加密方式 | 意義 | 注意 | 密鑰長度 | 應用 | | -------- | -------- | - | - | - | | 對稱加密 | 用同一把 Key 加密、解密 | 容易被集線器、交換器、路由器等等的中間設備盜取、攔截 | 通常 128、256 **(單位 Bit)** | DES、AES、3DES、IDEA、RC5 | | 非對稱加密 | 加密的密鑰稱為 public key (可與人共享),解密用 private key(需保密) | 較耗效能 | 與數學運算聯繫 **(單位 Bit)** | RSA、E1Gamal、Rabin | ### 單向認證 * 整個認證過程仍都是 **明文傳輸** * **會產生三次隨機數,前兩次是非對稱加密,==第三次產生的隨機數,是為了之後溝通用的對稱加密 Key==** :::success 1. 發送客戶端 SSL 版本訊息 (Client 可接受的 SSL) 2. 服務端給客戶端返回確定的 SSL 版本、**隨機數**、證書、公鑰訊息 3. 客戶端驗證證書合法性 4. 客戶端發送自己可支持的加密方法給服務器 5. 服務器將選擇好的加密方式交給客戶端 (明文) 6. 客戶端收到加密方式、**==產生隨機碼==,作為++對稱加密++密鑰,並使用 服務器公鑰 加密後**,將訊息加密後傳給服務端 7. 服務端使用 private key 解密,獲得對稱的密鑰 8. **最終使用對稱加密通訊** ::: > ![](https://i.imgur.com/Qd1k4Uq.png)來源於網路 ### 雙向認證 :::success 1. 發送客戶端 SSL 版本訊息 (Client 可接受的 SSL) 2. 服務端給客戶端返回確定的 SSL 版本、**隨機數**、證書、公鑰訊息 3. 客戶端驗證證書合法性,==發送客戶端的證書、公鑰== 4. ==服務器驗證客戶端證書== 5. 客戶端發送自己可支持的加密方法給服務器 6. 服務器將選擇好的加密方式交給客戶端 (明文) 7. 客戶端收到加密方式、**產生隨機碼,作為++對稱加密++密鑰,並使用 客戶端公鑰 加密後**,將訊息加密後傳給服務端 8. 服務端使用 private key 解密,獲得對稱的密鑰 9. **最終使用對稱加密通訊** ::: > ![](https://i.imgur.com/f87P2a4.png) 來源於網路 ## Socket 通訊 Socket 也就是套接字,**是針對 ==TCP/IP 協議封裝== 的接口 API**,用來描述 IP & Port :::info 其中網路通訊必須的訊息如下 1. 連接者所使用的協議 2. 本地主機 IP 地址 3. 要連接的協議端口 4. 遠端 IP 地址 5. IP 所使用的 Port ::: ### 簡單 Socket 通訊 > 以下會使用基礎的 Socket 功能 | API | 功能 | | -------- | -------- | | [**ServerSocket**](https://docs.oracle.com/javase/7/docs/api/java/net/ServerSocket.html) | 創建 Socket 服務端,可指定 Port | | [**Socket**](https://docs.oracle.com/javase/7/docs/api/java/net/Socket.html) | 創建 Socket 客戶端,需要指定 Host & Port | * 服務端程式如下:創建 ServerSocket ```java= import java.io.IOException; import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class SocketServer { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(22222); System.out.println("ServerSocket start accpect..."); Socket s = server.accept(); InputStream is = s.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr); String str; System.out.println("Hi, you met port is 22222, Start accpect Msg: \n"); while((str = br.readLine()) != null) { System.out.println(str); } br.close(); isr.close(); is.close(); //s.shutdownInput(); "1. " System.out.println("ServerSocket finish accpect !"); server.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` > 如果**已經手動關閉就不用調用 API 的 shutdownInput**,否則拋出錯誤 * 客戶端客戶端程式:透過 Socket 創建連線目標 ```java= import java.io.IOException; import java.io.*; import java.net.InetAddress; import java.net.Socket; public class SocketClient { public static void main(String[] args) { try { Socket client = new Socket("127.0.0.1", 22222); // "1. " //"2. " OutputStream os = client.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bw = new BufferedWriter(osw); InetAddress address = InetAddress.getLocalHost(); String ip = address.getHostAddress(); bw.write("Hello, I'm Socket Client, ip: " + ip); bw.flush(); client.shutdownOutput(); // 一定要 shutdown Output "3. " client.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` 1. 在創建 Socket 後就會 **自動 (不用調用 API)** 依照你設定的 host、port 連結 2. 並可獲取輸入輸出流 :::warning **必須要關閉資源才會真正輸出**(記得調用 `shutdownOutput`) ::: **--實作--** > ![](https://i.imgur.com/Dschqxc.png) ## Appendix & FAQ :::info ::: ###### tags: `網路基礎` `Http`