# WebSocket 技術 ###### tags: WebSocket, Java ## Client & Server 經由 Http 連線溝通,Client 端提出請求、Server 端提供 Response。 ![](https://i.imgur.com/KTPIwdE.png) ## 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),每一抽象層建立在低一層提供的服務上,並且為高一層提供服務。: ![](https://i.imgur.com/VCCKDSx.png) | 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 的確認,連線才會建立。 ![](https://i.imgur.com/A6vP3VR.png) 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 模型中大概介於這個位子: ![](https://i.imgur.com/7Vv738i.png) ## 範例:多人聊天室 ### index.html ![](https://i.imgur.com/FCOrkd8.png) 比較重要的程式碼如下: ```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` 都必須確實標記。 ![](https://i.imgur.com/BNHq9rf.png) 其中比較重要並與 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`)。