初識websocket及java服務端的簡單實現


概念:WebSocket是一種在單個TCP連接上進行全雙工通信的協議。

WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸

背景:很多網站為了實現推送技術,所用的技術都是輪詢。輪詢是在特定的的時間間隔(如每1秒),由瀏覽器對服務器發出HTTP請求,然后由服務器返回最新的數據給客戶端的瀏覽器。這種傳統的模式帶來很明顯的缺點,即瀏覽器需要不斷的向服務器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費很多的帶寬等資源。

而比較新的技術去做輪詢的效果是Comet。這種技術雖然可以雙向通信,但依然需要反復發出請求。而且在Comet中,普遍采用的長鏈接,也會消耗服務器資源。

在這種情況下,HTML5定義了WebSocket協議,能更好的節省服務器資源和帶寬,並且能夠更實時地進行通訊。

websocket運用場景:

  1. 即時通訊:多媒體聊天,你可以使用該技術開個聊天室,聊個火熱。可以單獨2人聊個暢快。
  2. 互動游戲:現在多人游戲越來越火熱,那么多人游戲必須是時時的,不考慮其他因素,只是時效性方面,也可以用該技術做多人游戲。
  3. 協同合作:開發人員會有git,svn等代碼管理工具,但還是會有沖突。用此技術開發一個文檔協同編輯工具,使每個人的編輯內容都時時同步,將不會有沖突發生。
  4. 動態數據表報:類似通知變更,如有需求,可以開發一個時時的數據報表,使用此技術,服務端數據發生變化,可在表報上立刻顯示出來。如,電商平台的交易數據,每時每刻都在變化着,可以時時監控。
  5. 實時工具:如導航,實時查詢工具等也可使用。

原理

WebSocket並不是全新的協議,而是利用了HTTP協議來建立連接。我們來看看WebSocket連接是如何創建的。

首先,WebSocket連接必須由瀏覽器發起,因為請求協議是一個標准的HTTP請求,格式如下:

GET ws://localhost:3000/ws/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Origin: http://localhost:3000
Sec-WebSocket-Key: client-random-string
Sec-WebSocket-Version: 13

該請求和普通的HTTP請求有幾點不同:

  1. GET請求的地址不是類似/path/,而是以ws://開頭的地址;
  2. 請求頭Upgrade: websocket和Connection: Upgrade表示這個連接將要被轉換為WebSocket連接;
  3. Sec-WebSocket-Key是用於標識這個連接,並非用於加密數據;
  4. Sec-WebSocket-Version指定了WebSocket的協議版本。

隨后,服務器如果接受該請求,就會返回如下響應:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: server-random-string

該響應代碼101表示本次連接的HTTP協議即將被更改,更改后的協議就是Upgrade: websocket指定的WebSocket協議。

為了創建Websocket連接,需要通過瀏覽器發出請求,之后服務器進行回應,這個過程通常稱為“握手”(handshaking)

版本號和子協議規定了雙方能理解的數據格式,以及是否支持壓縮等等。如果僅使用WebSocket的API,就不需要關心這些。

現在,一個WebSocket連接就建立成功,瀏覽器和服務器就可以隨時主動發送消息給對方。消息有兩種,一種是文本,一種是二進制數據。通常,我們可以發送JSON格式的文本,這樣,在瀏覽器處理起來就十分容易。

為什么WebSocket連接可以實現全雙工通信而HTTP連接不行呢?實際上HTTP協議是建立在TCP協議之上的,TCP協議本身就實現了全雙工通信,但是HTTP協議的請求-應答機制限制了全雙工通信。WebSocket連接建立以后,其實只是簡單規定了一下:接下來,咱們通信就不使用HTTP協議了,直接互相發數據吧。

HTTP協議與websocket協議對比:

 

WebSocket目前支持兩種統一資源標志符ws和wss,類似於HTTP和HTTPS。

 

瀏覽器支持:

很顯然,要支持WebSocket通信,瀏覽器得支持這個協議,這樣才能發出ws://xxx的請求。目前,支持WebSocket的主流瀏覽器如下:

  • Chrome
  • Firefox
  • IE >= 10
  • Sarafi >= 6
  • Android >= 4.4
  • iOS >= 8

 

客戶端簡單實現:

新建websocket.html

 

<!DOCTYPE HTML>
<html>
   <head>
   <meta charset="utf-8">
   <title>websocket 客戶端</title>
    
      <script type="text/javascript">
         function WebSocketTest() { if ("WebSocket" in window) { alert("您的瀏覽器支持 WebSocket!"); // 創建一個 websocket
               var ws = new WebSocket("ws://localhost:8887"); ws.onopen = function() { // Web Socket 已連接上,使用 send() 方法發送數據
 ws.send("發送數據"); alert("數據發送中..."); }; ws.onmessage = function (evt) { var received_msg = evt.data; alert("數據已接收..."); }; ws.onclose = function() { // 關閉 websocket
 alert("連接已關閉..."); }; } else { // 瀏覽器不支持 WebSocket
 alert("您的瀏覽器不支持 WebSocket!"); } } </script>
        
   </head>
   <body>
   
      <div id="sse">
         <a href="javascript:WebSocketTest()">運行 WebSocket</a>
      </div>
      
   </body>
</html>

 

 

客戶端API:

WebSocket 屬性

以下是 WebSocket 對象的屬性。假定我們使用了以上代碼創建了 Socket 對象:

屬性

描述

Socket.readyState

只讀屬性 readyState 表示連接狀態,可以是以下值:

0 - 表示連接尚未建立。

1 - 表示連接已建立,可以進行通信。

2 - 表示連接正在進行關閉。

3 - 表示連接已經關閉或者連接不能打開。

Socket.bufferedAmount

只讀屬性 bufferedAmount 已被 send() 放入正在隊列中等待傳輸,但是還沒有發出的 UTF-8 文本字節數。

WebSocket 事件

以下是 WebSocket 對象的相關事件。假定我們使用了以上代碼創建了 Socket 對象:

事件

事件處理程序

描述

open

Socket.onopen

連接建立時觸發

message

Socket.onmessage

客戶端接收服務端數據時觸發

error

Socket.onerror

通信發生錯誤時觸發

close

Socket.onclose

連接關閉時觸發

WebSocket 方法

以下是 WebSocket 對象的相關方法。假定我們使用了以上代碼創建了 Socket 對象:

方法

描述

Socket.send()

使用連接發送數據

Socket.close()

關閉連接

 

JAVA 服務端實現:

maven依賴

<dependency>
  <groupId>org.java-websocket</groupId>
  <artifactId>Java-WebSocket</artifactId>
  <version>1.4.0</version>
</dependency>

  

 

服務端示例:ServerDemo.java

import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;

import java.net.InetSocketAddress;
import java.net.UnknownHostException;

/**
 * @Author 99514925@qq.com
 * @Create 2020/2/3 23:03
 **/
public class ServerDemo extends WebSocketServer {


    public ServerDemo() throws UnknownHostException {
    }

    public ServerDemo(int port) throws UnknownHostException {
        super(new InetSocketAddress(port));
        System.out.println("websocket Server start at port:"+port);
    }


    /**
     * 觸發連接事件
     */
    @Override
    public void onOpen(WebSocket conn, ClientHandshake clientHandshake) {
        System.out.println("new connection ===" + conn.getRemoteSocketAddress().getAddress().getHostAddress());
    }

    /**
     *
     * 連接斷開時觸發關閉事件
     */
    @Override
    public void onClose(WebSocket conn, int code, String reason, boolean remote) {

    }

    /**
     * 客戶端發送消息到服務器時觸發事件
     */
    @Override
    public void onMessage(WebSocket conn, String message) {
        System.out.println("you have a new message: "+ message);
        //向客戶端發送消息
        conn.send(message);
    }

    /**
     * 觸發異常事件
     */
    @Override
    public void onError(WebSocket conn, Exception e) {
        //e.printStackTrace();
        if( conn != null ) {
            //some errors like port binding failed may not be assignable to a specific websocket
        }
    }

    /**
     * 啟動服務端
     * @param args
     * @throws UnknownHostException
     */
    public static void main(String[] args) throws UnknownHostException {
        new ServerDemo(8887).start();
    }
}

 

 

由於WebSocket是一個協議,服務器具體怎么實現,取決於所用編程語言和框架本身。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM