天天看點

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 的簡單應用入門

背景測試

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

總結

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

擴充:一款非常特别的 WebSocket 伺服器:Websocketd。

它的最大特點,就是背景腳本不限語言,标準輸入(stdin)就是 WebSocket 的輸入,标準輸出(stdout)就是 WebSocket 的輸出。具體使用介紹http://www.ruanyifeng.com/blog/2017/05/websocket.html

WebSocket 的簡單應用入門

參考來源于:

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

繼續閱讀