# WebSocket 技術
###### tags: WebSocket, Java
## Client & Server
經由 Http 連線溝通,Client 端提出請求、Server 端提供 Response。

## Client Pull(使用者擷取)
Client 端發出請求,才會獲得資料。明顯的缺點:
1. **獲得的資料不夠即時。**
為了解決這個狀況,一直對伺服器發出請求(輪詢)。使用者為了得到最新資訊,一直使用裝置刷新,對伺服器發出請求,更產生以下缺點:
2. **過多的操作(向伺服器發出請求)導致電量損耗(客戶端裝置)。**
3. **對 Server 端的負擔增加。**
## Server Push(伺服器推播)
為了解決 Client Pull 的問題,反過來讓 Server 主動在資料更新時發送給客戶端。
### 使用推播
* 許多廠商都有實作推播伺服器,可以使用其 API。
* Java EE7 制定有推播的通訊標準 ==**WebSocket**== API,可以自行實作。
* JavaScript、PHP...等都有 WebSocket 的 API 可供程式設計師實作。
## WebSocket
「Socket」,有人稱之為「網路插座」,基於網路通訊使用的機制,當插頭找到插座,就會開啟資料傳輸的通道。
* 約1980年代時出現,由柏克萊大學制定標準。
* 最早是用在 Unix 作業系統,作為[**行程(Process)**](https://hackmd.io/OisZ0l--R9Ofc4p1rrnR4Q?view#程序(Process)、執行緒(Thread)與資源分配)**之間**(分店之間)的通訊。
* 是一種主從式(Client-Server)的架構,實現**雙向**的資料傳輸。
### 網際網路概述
網際網路最常見的網路通訊模型(TCP/IP),有四層(4 layers),每一抽象層建立在低一層提供的服務上,並且為高一層提供服務。:

| Layer| 網際網路協定疊中的層 | 例子或定義標準(協定) |
| -------- | -------- | --- |
| 4 | 應用層 application layer |HTTP、FTP、POP3、SMTP、SSH |
| 3 | 傳輸層 transport layer |TCP、UDP |
| 2 | 網路層 internet layer |IP |
| 1 | 實體鏈結層 Network Access (link) layer |網卡、網路線、Wifi |
:::warning
**IP**:
<div style="margin-bottom:16px;" title="保持間距用"></div>
1. IPv4 為例,以 0~255 之間的四組號碼排列組合,面對可以上網的裝置變多,已發配完畢,因此誕生了 IPv6 的格式,以 6 組 16 進位的數字去發配。
2. **public IP(公開網路) -> private IP(區域網路)**:
類比 Tibame 的公司代表電話號碼為 public IP,轉接分機則為 private IP。故連自己家的 wifi,所拿到的 IP 僅限於連這個 wifi 底下的裝置內部連接存取。
:::
:::warning
**TCP**:
<div style="margin-bottom:16px;" title="保持間距用"></div>
1. **連線導向**:連線為導向的資料傳輸(一定要跟對方建立連線,才能夠開始傳資料)。
JDBC 也以 TCP 協定進行傳輸。
有[**三向交握**](https://notfalse.net/7/three-way-handshake)機制:舉例而言,A 向 B 發出「確認是否為對方」的訊息,B 會向 A 傳達「對這個確認的回應」與「確認是否為對方」的訊息,A 會再回應 B 的確認,連線才會建立。

2. **可靠性高**:每個封包都會確認交握才會傳輸。中間若有漏掉或中斷,則不會繼續傳輸。衍生下一個特性:
3. **傳輸時間較長、較耗資源**。
**UDP**:與 TCP 特性相反。
<div style="margin-bottom:16px;" title="保持間距用"></div>
1. **可靠性低**:只要知道目的地是哪裡就會開始傳輸了。A 送給 B,B 有沒有收下、有沒有漏掉,A 一概不管,繼續傳。
2. **資料的即時性**大於可靠性時就會使用 UDP。如:網路電話要即時傳輸,但中間可能會斷訊,後面的還是會繼續傳輸。
:::
:::warning
**應用層**:
<div style="margin-bottom:16px;" title="保持間距用"></div>
應用層本身並不屬於應用程式所有,而是在定義應用程式如何進入此層的溝通介面,以將資料接收或傳送給應用程式,最終展示給使用者。
:::
WebSocket 使用 TCP 傳輸,但在 TCP/IP 模型中大概介於這個位子:

## 範例:多人聊天室
### index.html

比較重要的程式碼如下:
```javascript=
// 前略...
// 變數宣告
var MyPoint = "/Together/james";
var webCxt = path.substrin(0, path.indexOf('/', 1));
var endPointURL = "ws://" + window.location.host + webCtx + MyPoint;
var webSocket;
// 連線建立時,產生一個 WebSocket 物件
webSocket = new WebSocket(endPointURL);
```
產生物件的同時,就以參數 endPointURL(以 **WebSocket 通訊協定**與請求 URL 網址 `ws://localhost:8081/WebSocketChatWeb/TogetherWS/james`,其中包含連結端點的 url-pattern `/Together/james`)連接至 TogetherWS.java (Server 端)的 ServerEndpoint。
當然,可以成功連接的前提是:TogetherWS.java 有標註/註冊此端點的 url-pattern。
### TogetherWS.java
範例內的所有`@annotation` 都必須確實標記。

其中比較重要並與 index.html 相關的程式碼:
```java=
@ServerEndpoint("/Together/{userName}")
```
ServerEndpoint 會將其後 `()` 內的 URL 字串標示為 Server 端 WebSocket 連結的端點。Client 端網頁(index.html 檔案)只要在產生 WebSocket 物件時,輸入同樣的連接端點 URL(**但必須是包含通訊協定 ws 與 URL 的拜訪路徑**),就可以連入。
`ws://localhost:8081/WebSocketChatWeb/` 不必完整寫出,和 Servlet `@annotation` 註冊的寫法很像,會由 `/Together/james` 中第一個 `/` 自行由 Web App專案路徑(也就是大吳老師說的「教室」、「IBM_9」)開始,往前穿透至伺服器根目錄與通訊協定 `ws://` 為止。
在這份範例中,index.html 將 `MyPoint` 變數指定的值寫死為 `"/Together/james"`,所以若開啟 index.html,可以看到 userName 在 TogetherWS.java 取得的值(顯示在 console)為 `james`。
意即,所有人打開這支 index.html 時,永遠都會以 james 進入聊天室(多人聊天室的效果)。
小吳老師在這邊將 `@ServerEndpoint()` 參數註明為 `"/Together/{userName}"` 是為了讓我們知道這支程式若在 index.html 端動態設定使用者名稱為變數,如:
```javascript=
var userName = xxx; // 或動態去抓取的某值,如 userNameList 的 userName;
var MyPoint = "/Together/" + userName;
```
則開啟 index.html 並鍵入後,開啟的所有人都會進入聊天室,只是顯示名稱有所不同( `userName`)。