1. 程式人生 > >WebSocket 的簡單應用入門

WebSocket 的簡單應用入門

websocket簡單實現分為以下幾個步驟:新增websocket庫編寫後臺程式碼編寫前端程式碼


新增websocket庫

在maven中新增websocket庫的程式碼如下所示:

<dependency>
   <groupId>javax.websocket</groupId>
   <artifactId>javax.websocket-api</artifactId>
   <version>1.1</version>
   <scope>provided</scope>
</dependency>

 注:九風有次沒寫<scope>欄位,前端後臺都會報錯,大家記得加上就行。


 

WebSocket 客戶端的實現

一,WebSocket 客戶端的簡單示例

WebSocket 的用法比較簡單,下面是一個網頁尾本的例子:

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};      

 

二,WebSocket 客戶端的 API 

  • WebSocket 建構函式

WebSocket 物件作為一個建構函式,用於新建 WebSocket 例項

var ws = new WebSocket('ws://localhost:8080');

執行上面語句之後,客戶端就會與伺服器進行連線。


  • webSocket.readyState

readyState屬性返回例項物件的當前狀態,共有四種。

  1. CONNECTING:值為0,表示正在連線。
  2. OPEN:值為1,表示連線成功,可以通訊了。
  3. CLOSING:值為2,表示連線正在關閉。
  4. CLOSED:值為3,表示連線已經關閉,或者開啟連線失敗。

下面是一個示例:

switch (ws.readyState) {
  case WebSocket.CONNECTING:
    // do something
    break;
  case WebSocket.OPEN:
    // do something
    break;
  case WebSocket.CLOSING:
    // do something
    break;
  case WebSocket.CLOSED:
    // do something
    break;
  default:
    // this never happens
    break;
}

  • webSocket.onopen

例項物件的onopen屬性,用於指定連線成功後的回撥函式。

ws.onopen = function () {
  ws.send('Hello Server!');
}

如果要指定多個回撥函式,可以使用addEventListener方法

ws.addEventListener('open', function (event) {
  ws.send('Hello Server!');
});


  • webSocket.onclose

例項物件的onclose屬性,用於指定連線關閉後的回撥函式。

ws.onclose = function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
};

ws.addEventListener("close", function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  // handle close event
});


  • webSocket.onmessage

例項物件的onmessage屬性,用於指定收到伺服器資料後的回撥函式。

ws.onmessage = function(event) {
  var data = event.data;
  // 處理資料
};

ws.addEventListener("message", function(event) {
  var data = event.data;
  // 處理資料
});

注意,伺服器資料可能是文字,也可能是二進位制資料blob物件或Arraybuffer物件)。

ws.onmessage = function(event){

 // 動態判斷收到的資料型別
  if(typeof event.data === String) {
    console.log("Received data string");
  }

 // 動態判斷收到的資料型別

  if(event.data instanceof ArrayBuffer){
    var buffer = event.data;
    console.log("Received arraybuffer");
  }
}

除了動態判斷收到的資料型別,也可以使用binaryType屬性顯式指定收到的二進位制資料型別

// 收到的是 blob 資料
ws.binaryType = "blob";
ws.onmessage = function(e) {
  console.log(e.data.size);
};

// 收到的是 ArrayBuffer 資料
ws.binaryType = "arraybuffer";
ws.onmessage = function(e) {
  console.log(e.data.byteLength);
};


  • webSocket.send()

 例項物件的send()方法用於向伺服器傳送資料

1,傳送文字的例子:

ws.send('your message');

2,傳送 Blob 物件的例子:

var file = document
  .querySelector('input[type="file"]')
  .files[0];
ws.send(file);

3,傳送 ArrayBuffer 物件的例子:

// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
  binary[i] = img.data[i];
}
ws.send(binary.buffer);


  • webSocket.bufferedAmount

例項物件的bufferedAmount屬性,表示還有多少位元組的二進位制資料沒有傳送出去它可以用來判斷髮送是否結束

var data = new ArrayBuffer(10000000);
socket.send(data);

if (socket.bufferedAmount === 0) {
  // 傳送完畢
} else {
  // 傳送還沒結束
}


  • webSocket.onerror

例項物件的onerror屬性,用於指定報錯時的回撥函式。

socket.onerror = function(event) {
  // handle error event
};

socket.addEventListener("error", function(event) {
  // handle error event
});


實際應用前端程式碼如下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        websocket Demo---- user000 <br />
        <input id="text" type="text" /> 
        <button onclick="send()"> Send </button>   
        <button   onclick="closeWebSocket()"> Close </button>
        <div id="message">   </div>
        
    <script type="text/javascript">
     //判斷當前瀏覽器是否支援WebSocket
      if('WebSocket' in window){
          websocket = new WebSocket("ws://localhost:8080/Demo/websocketTest/user000");
          console.log("link success")
      }else{
          alert('Not support websocket')
      }
      
      //連線發生錯誤的回撥方法
      websocket.onerror = function(){
          setMessageInnerHTML("error");
      };
       
      //連線成功建立的回撥方法
      websocket.onopen = function(event){
          setMessageInnerHTML("open");
      }
       console.log("-----")
      //接收到訊息的回撥方法
      websocket.onmessage = function(event){
            setMessageInnerHTML(event.data);
      }
       
      //連線關閉的回撥方法
      websocket.onclose = function(){
          setMessageInnerHTML("close");
      }
       
      //監聽視窗關閉事件,當視窗關閉時,主動去關閉websocket連線,防止連線還沒斷開就關閉視窗,server端會拋異常。
      window.onbeforeunload = function(){
          websocket.close();
      }
       
      //將訊息顯示在網頁上
      function setMessageInnerHTML(innerHTML){
          document.getElementById('message').innerHTML += innerHTML + '<br/>';
      }
       
      //關閉連線
      function closeWebSocket(){
          websocket.close();
      }
       
      //傳送訊息
      function send(){
          var message = document.getElementById('text').value;
          websocket.send(message);
      }
    </script>
        
    </body>
</html>

 

WebSocket 服務端的實現

後臺實現websocket有兩種方式:使用繼承類、使用註解;註解方式比較方便,一下程式碼中使用註解方式來進行演示。

  • @ServerEndpoint

宣告websocket地址類似Spring MVC中的@controller註解類似,websocket使用@ServerEndpoint來進行宣告介面:@ServerEndpoint(value="/websocket/{paraName}") ; 其中 “ { } ”用來表示帶引數的連線,如果需要獲取{}中的引數在引數列表中增加:@PathParam("paraName") Integer userId 。


1,@OnOpen

public void onOpen(Session session) throws IOException{ }-------有連線時的觸發函式。 我們可以在使用者連線時記錄使用者的連線帶的引數,只需在引數列表中增加引數:@PathParam("paraName") String paraName。

擴充套件:

其中OnOpen發生的時候,即有連結過來的時候,可以把當前WebSocket Server丟在ServerManager裡管理起來,這樣Tomcat才知道總共有哪些Server, 方便以後進行群發,具體案例http://how2j.cn/k/websocket/websocket-develop/1628.html#nowhere


2,@OnClose

public void onClose(){ }------連線關閉時的呼叫方法。


3,@OnMessage

public void onMessage(String message, Session session) { }-------收到訊息時呼叫的函式,其中Session是每個websocket特有的資料成員


4,Session----每個Session代表了兩個web socket斷點的會話;當websocket握手成功後,websocket就會提供一個開啟的Session,可以通過這個Session來對另一個端點發送資料;如果Session關閉後傳送資料將會報錯。


5,Session.getBasicRemote().sendText("message")-------向該Session連線的使用者傳送字串資料。


6,@OnError

public void onError(Session session, Throwable error) { }--------發生意外錯誤時呼叫的函式。


實際應用後端程式碼如下:

import java.io.IOException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** 
 * @Class: Test
 * @Description: 簡單websocket demo
 * @author 九風萍舟
 */
@ServerEndpoint(value="/websocketTest/{userId}")
public class Test {
    private Logger logger = LoggerFactory.getLogger(Test.class);
    
    private static String userId;
    
    //連線時執行
    @OnOpen
    public void onOpen(@PathParam("userId") String userId,Session session) throws IOException{
        this.userId = userId;
        logger.debug("新連線:{}",userId);
    }
    
    //關閉時執行
    @OnClose
    public void onClose(){
        logger.debug("連線:{} 關閉",this.userId);
    }
    
    //收到訊息時執行
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        logger.debug("收到使用者{}的訊息{}",this.userId,message);
        session.getBasicRemote().sendText("收到 "+this.userId+" 的訊息 "); //回覆使用者
    }
    
    //連線錯誤時執行
    @OnError
    public void onError(Session session, Throwable error){
        logger.debug("使用者id為:{}的連線傳送錯誤",this.userId);
        error.printStackTrace();
    }

}

ServerEndpoint報錯: 原因是不能自動檢測 ServerEndpoint 的包,解決方法:複製 import javax.websocket.server.ServerEndpoint; 到檔案程式 import 區域即可。


 

測試執行

在Chrome上開啟前端程式碼後,馬上就建立了連線,大家可以使用F12檢視下建立連線的請求與響應,可以對比前面關於協議建立的部分進行學習。
建立連線後,想後臺傳送資料後,同時可以看到後臺返回的資訊:

前端測試

在後臺可以看到連線的建立和收到的資料:

後臺測試

對於其他功能功能大家可以自己測測。

總結

websocket特別適合於需要實時資料傳送的場景,比輪詢方式效率高很多。


 

擴充套件:一款非常特別的 WebSocket 伺服器:Websocketd

它的最大特點,就是後臺指令碼不限語言,標準輸入(stdin)就是 WebSocket 的輸入,標準輸出(stdout)就是 WebSocket 的輸出。具體使用介紹http://www.ruanyifeng.com/blog/2017/05/websocket.html

 


參考來源於:

http://www.ruanyifeng.com/blog/2017/05/websocket.html

https://www.jianshu.com/p/d79bf8174196

https://www.java-mindmap.com/channel/7

http://how2j.cn/k/websocket/websocket-develop/1628.html#nowhere