---
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 可以代表協議,以及要訪問的資源位置、設定
>
> 
### 協定 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
> 
* **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);
}
```
> 
* 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 的文本**
> 
## 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"}
```
>
> 
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>
```
> 
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` 就可以見到)
> 
## 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` 規則存入本地的緩存**,之後每次請求都會進行比對,這樣才不會浪費資源
> 
* **強制緩存**,每次比對都會本地緩存,比對 **==expires== (Http\1.0)、private、public、max-age、no-store (每次都請求數據)、no-cache (對比緩存)**,**當數據已過期就會再次請求伺服器,否則會繼續使用緩存資源**
> 
強制緩存中的 `cache-control` ==**使用 no-cache 緩存**== 機制,需要透過 **++對比緩存++**,**成功返回 304 代表不用更新數據** (服務端 & 客戶端都要支持)
> 
### 對比緩存
* 對比緩存一定有一個**對比的標示**,而**決定該標示是否有用在於伺服器**,**而該標示也有優先級**,該標示如下表
| 優先級 | 對比標示 | 功能 | 作用 |
| - | -------- | -------- | -------- |
| **低** | `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 |
> 
## 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. **最終使用對稱加密通訊**
:::
> 來源於網路
### 雙向認證
:::success
1. 發送客戶端 SSL 版本訊息 (Client 可接受的 SSL)
2. 服務端給客戶端返回確定的 SSL 版本、**隨機數**、證書、公鑰訊息
3. 客戶端驗證證書合法性,==發送客戶端的證書、公鑰==
4. ==服務器驗證客戶端證書==
5. 客戶端發送自己可支持的加密方法給服務器
6. 服務器將選擇好的加密方式交給客戶端 (明文)
7. 客戶端收到加密方式、**產生隨機碼,作為++對稱加密++密鑰,並使用 客戶端公鑰 加密後**,將訊息加密後傳給服務端
8. 服務端使用 private key 解密,獲得對稱的密鑰
9. **最終使用對稱加密通訊**
:::
>  來源於網路
## 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`)
:::
**--實作--**
> 
## Appendix & FAQ
:::info
:::
###### tags: `網路基礎` `Http`