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
屬性傳回執行個體對象的目前狀态,共有四種。
- CONNECTING:值為0,表示正在連接配接。
- OPEN:值為1,表示連接配接成功,可以通信了。
- CLOSING:值為2,表示連接配接正在關閉。
- 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