目錄
- 一、 websocket 概念
- 二、WebSocket原理
- 三、WebSocket特點
- 四、WebSocket應用場景
- 五、Websocket基本使用
- 1、創建Websocket對象
- 2、Websocket事件
- 3、Websocket方法
- 4、前端服務程序
- 六、聊天室案例
- 1、Tomcat版本:8.0.44
- 2、Maven 依賴:
- 3、前端代碼
- 4、后端代碼
一、 websocket 概念
WebSocket是HTML5提供的一種瀏覽器與服務器進行全雙工通訊的網絡技術,屬于應用層協議。它基于TCP傳輸協議,并復用HTTP的握手通道。瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接, 并進行雙向數據傳輸。WebSocket 的出現就解決了半雙工通信的弊端。它最大的特點是:服務器可以向客戶端主動推動消息,客戶端也可以主動向服務器推送消息。
獲取到最新的信息,只有再次發起客戶端請求,服務器端才會返回結果。但是服務器端不能做到推送消息給客戶端,當然我們可以使用輪詢,查看服務器有沒有新的消息
二、WebSocket原理
客戶端向 WebSocket 服務器通知(notify)一個帶有所有接收者ID(recipients IDs)的事件(event),服務器接收后立即通知所有活躍的(active)客戶端,只有ID在接收者ID序列中的客戶端才會處理這個事件。
三、WebSocket特點
- 支持雙向通信,實時性更強,相對于HTTP請求需要等待客戶端發起請求服務端才能響應,延遲明顯更少
- 可以發送文本,也可以發送二進制數據
- 建立在TCP協議之上,服務端的實現比較容易
- 數據格式比較輕量,性能開銷小,通信高效
- 沒有同源限制,客戶端可以與任意服務器通信
- 協議標識符是ws(如果加密,則為wss),服務器網址就是 URL
- 與 HTTP 協議有著良好的兼容性。默認端口也是80和443,并且握手階段采用 HTTP 協議,因此握手時不容易屏蔽,能通過各種 HTTP 代理服務器
四、WebSocket應用場景
- 彈幕
- 媒體聊天
- 協同編輯
- 基于位置的應用
- 體育實況更新
- 股票基金報價實時更新
五、Websocket基本使用
在HTML5中,瀏覽器已經實現了websocket的API,直接使用即可:WebSocket-MDN
1、創建Websocket對象
// 參數1: url:連接的websocket屬性
// 參數2: protocol,可選的,指定連接的協議
// var socket = new WebSocket('ws://echo.websocket.org')
var Socket = new WebSocket(url, [protocol] );
2、Websocket事件
事件 | 事件處理程序 | 描述 |
---|---|---|
open | Socket.onopen | 連接建立時觸發 |
message | Socket.onmessage | 客戶端接收服務端數據時觸發 |
error | Socket.onerror | 通信發生錯誤時觸發 |
close | Socket.onclose | 連接關閉時觸發 |
3、Websocket方法
方法 | 描述 |
---|---|
Socket.send() | 使用連接發送數據 |
Socket.close() | 關閉連接 |
WebSocket的狀態
可以通過讀取readyState的屬性值來獲取WebSocket對象的狀態,readyState屬性存在以下幾種屬性值。
- CONNECTING(數字值為0),表示正在連接;
- OPEN(數字值為1),表示已建立連接;
- CLOSING(數字值為2),表示正在關閉連接;
- CLOSED(數字值為3),表示已關閉鏈接。
4、前端服務程序
// 導入第三方模塊
const ws = require('nodejs-websocket')
var websocket = new WebSocket("ws://172.16.116.22:8088/WebSocket/chatServer/"+nickname);
// websocket占用的端口號
const PORT = 3000const server = ws.createServer(connect => {console.log('新的連接')// 接收到客戶端的文本內容時觸發connect.on('text', str => {console.log('接收:' + str)// 把接收到的字符串轉換成大寫,并且給客戶端響應connect.sendText(str.toUpperCase() + '!!!!')})// 監聽關閉事件connect.on('close', () => {console.log('連接關閉了')})// 監聽錯誤事件, 比如瀏覽器關閉了連接,或者發送的數據格式不對等connect.on('error', err => {console.log('連接異常')})
})// 啟動websocket服務
server.listen(PORT, function() {console.log(`websocket server listening on ${PORT}`)
})
WebSocket的前端API
-
WebSocket需要接收一個url參數,然后調用WebSocket對象的構造器來建立與服務器之間的通信鏈接。
websocket = new WebSocket(“ws://localhost:8088/chatServer/”+nickname);- URL字符串必須以 “ws” 或 “wss”(加密通信)開頭。
- 利用上面的代碼,我們的通信連接建立之后,就可以進行客戶端與服務器端的雙向通信了。可以使用WebSocket對象的send方法對服務器發送數據,但是只能發送文本數據(我們可以使用JSON對象把任何js對象轉換成文本數據后再進行發送)。
-
websocket.send(“data”);
- websocket.send(“data”);
-
websocket.onmessage = function(event) {
websocket.onmessage = function(event) {var data = event.data; }
-
監聽socket的打開事件
- 通過獲取onopen事件來監聽socket的打開事件。如下代碼:
websocket.onopen = function(event) { // 開始通信時的處理 }
- 通過獲取onopen事件來監聽socket的打開事件。如下代碼:
-
通過獲取onclose事件來監聽socket的關閉事件。如下代碼:
websocket.onclose = function(event) { // 通信結束時的處理 }
-
關閉socket
通過close方法來關閉socket, 如下代碼:
websocket.close();
六、聊天室案例
1、Tomcat版本:8.0.44
2、Maven 依賴:
<dependency><groupId>javax.websocket</groupId><artifactId>javax.websocket-api</artifactId><version>1.1</version><scope>provided</scope></dependency>
<!-- <dependency>-->
<!-- <groupId>javax</groupId>-->
<!-- <artifactId>javaee-api</artifactId>-->
<!-- <version>8.0</version>-->
<!-- </dependency>--><!-- 導入servlet-->
<!-- 處理JSON-->
<!-- <dependency>-->
<!-- <groupId>com.alibaba</groupId>-->
<!-- <artifactId>fastjson</artifactId>-->
<!-- <version> 1.2.58</version>-->
<!-- </dependency>--><dependency><groupId>com.google.code.gson</groupId><artifactId>gson</artifactId><version>2.8.5</version></dependency>
3、前端代碼
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>聊天室</title><link rel="stylesheet" href=""><style type="text/css" media="screen">*{margin: 0px;padding: 0px;}.all{width: 900px;height: 700px;margin: 0 auto;}.all-top{width: 100%;height: 30px;background-color: yellow;}.all-center{width: 100%;height: 570px;/*添加滾動條*/overflow-y: scroll;background-color: pink;}.all-bottom{width: 100%;height: 100px;background-color: green;}.number{width: 100%;height: 40px;background-color: green;text-align: center;color: #fff;font-size: 20px;}</style>
</head>
<body>
<div class="all"><div class="all-top">昵稱:<input type="text" id="nickname"><button type="button" onclick="connect()">鏈接</button></div><div class="number" id="number">當前在線人數:0人</div><div class="all-center" id="message">IP:172.16.116.22 <br>端口是:8088<br>工程是:WebSocket<br>接口是:chatServer<br></div><div class="all-bottom">發送信息:<input type="text" id="msg"><button type="button" onclick="sendMsg()">發送</button></div>
</div>
</body>
<script type="text/javascript">/*ws:// 代表 websocket*//*127.0.0.1:8088 后端服務地址*//*WebSocket 工程名稱*//*chatServer 對應@ServerEndpoint注解的value屬性*/var websocket = null;//鏈接后端服務器function connect(){if(websocket != null){return;}var nickname = document.getElementById("nickname").value;console.log(nickname)websocket = new WebSocket("ws://localhost:8088/chatServer/"+nickname);//鏈接成功時觸發websocket.onopen = function(){setMsgToPage("你鏈接上聊天服務器了!");}//收到服務器端信息觸發websocket.onmessage = function(event){var data = JSON.parse(event.data)if(data.code == 1){//接收到的是聊天信息var umsg = data.name + "說:" + data.msg;setMsgToPage(umsg);}else if(data.code == 2){//上線document.getElementById("number").innerHTML = "當前在線人數:"+data.number+"人";setMsgToPage("艦長:"+data.name+"進入直播間");}else if(data.code == 3){//下線document.getElementById("number").innerHTML = "當前在線人數:"+data.number+"人";setMsgToPage("艦長:"+data.name+"離開直播間");}}//發生異常時觸發websocket.onerror = function(){}//鏈接關閉觸發websocket.onclose = function(){}}//用來給后端發送信息function sendMsg(){//獲取用戶輸入的信息var msg = document.getElementById("msg").value;//給后端發送websocket.send(msg);}//把信息賦值到頁面function setMsgToPage(msg){document.getElementById("message").innerHTML += msg + "</br>";}</script>
</html>
4、后端代碼
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;//WebSocket服務
//value是WebSocket接口地址
@ServerEndpoint(value="/chatServer/{userName}")
public class ChatServer {//session使用來保存用戶狀態信息的//CopyOnWriteArraySet == set 但是CopyOnWriteArraySet是線程安全的private static CopyOnWriteArraySet<Session> socketSet = new CopyOnWriteArraySet<Session>();//1.0用戶連接時觸發@OnOpenpublic void onOpen(@PathParam("userName")String name,Session session){System.out.println("用戶開始鏈接");//Session 用戶的信息 用戶的狀態socketSet.add(session);for(Session s:socketSet){//給所有在集合內的用戶廣播信息try {if(s.isOpen()){s.getBasicRemote().sendText("{\"code\":2,\"number\":\""+socketSet.size()+"\",\"name\":\""+name+"\"}");}else{s.close();}} catch (IOException e) {e.printStackTrace();}}}//2.0接收到用戶信息時觸發@OnMessagepublic void onMessage(@PathParam("userName")String name,String data,Session session){for(Session s:socketSet){//給所有在集合內的用戶廣播信息try {if(s.isOpen()){s.getBasicRemote().sendText("{\"code\":1,\"name\":\""+name+"\",\"msg\":\""+data+"\"}");}else{s.close();}} catch (IOException e) {e.printStackTrace();}}}//3.0用戶下線時觸發@OnClosepublic void onClose(@PathParam("userName")String name,Session session){//用戶下線時刪除這個socketSet.remove(session);for(Session s:socketSet){//給所有在集合內的用戶廣播信息try {if(s.isOpen()){s.getBasicRemote().sendText("{\"code\":3,\"number\":\""+socketSet.size()+"\",\"name\":\""+name+"\"}");}else{s.close();}} catch (IOException e) {e.printStackTrace();}}}//4.0連接異常時觸發
}