websocket實現類似於QQ的聊天功能
阿新 • • 發佈:2019-02-14
使用websocket實現聊天的原因
- http單向通訊協議,請求只能是客戶端發起,且是無狀態的,而websocket是雙向通訊協議,可以有伺服器發起也可以是客戶端發起,用http實現聊天功能一般是通過輪詢,但是輪詢非常浪費伺服器資源,而且慢,親測過
- 重點websocket實現簡單,速度快
上程式碼
後臺程式碼:採用websocket的註解
package com.xh.procourt.controller; /** * Created: Administrator * author:xmf * Date:2018/7/20 * Time:9:49 */ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.mybatis.spring.annotation.MapperScan; import org.springframework.web.context.ContextLoader; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * @ServerEndpoint 註解是一個類層次的註解,它的功能主要是將目前的類定義成一個websocket伺服器端,value是客戶端連線地址,這裡定義了兩個引數,和springmvc的配置指定格式的URL, * 對映到對應的引數是一樣的 */ @ServerEndpoint(value = "/websocket/{msg_me}")//引數格式 自定義連線標識 public class WebSocketCt { //靜態變數,用來記錄當前線上連線數。應該把它設計成執行緒安全的。 private static int onlineCount = 0; //用來存放每個客戶端對應的MyWebSocket物件。若要實現服務端與單一客戶端通訊的話,可以使用Map來存放,其中Key可以為使用者標識類似於扣扣號,map是執行緒不安全的 private static Map<String, Session> webSocketSet = new HashMap<>(); /** * 連線建立成功呼叫的方法 * * @param session 可選的引數。session為與某個客戶端的連線會話,需要通過它來給客戶端傳送資料 @PathParam用來獲取連線路徑上的引數 */ @OnOpen public void onOpen(@PathParam(value = "msg_me") String param, Session session) { webSocketSet.put(param, session); //加入map中 addOnlineCount(); //線上數加1 System.out.println("標識為----" + param + "----的使用者連線成功,當前線上人數:::" + getOnlineCount()); //用map構造訊息格式 懶得建立訊息實體類 Map<String, Object> map = new HashMap<>(); map.put("senderId", "system"); map.put("msgContent", "歡迎連線"); //由於這裡使用的string的訊息型別所以在這裡轉成json,當然還有其它訊息型別 try { sendMessage(JSON.toJSON(map).toString(), session); } catch (IOException e) { e.printStackTrace(); } } /** * 連線關閉呼叫的方法 */ @OnClose public void onClose(@PathParam(value = "msg_me") String param) { //根據自定義的標識移除此使用者的連線 webSocketSet.remove(param); subOnlineCount(); //線上數減1 System.out.println("標識為----" + param + "----的使用者斷開連線成功,當前線上人數:::" + getOnlineCount()); } /** * 收到客戶端訊息後呼叫的方法 * * @param message 客戶端傳送過來的訊息 格式傳送人_接收人_訊息 * @param session 可選的引數 */ @OnMessage public void onMessage(String message, Session session, @PathParam(value = "msg_me") String fromId) { System.out.println("收到訊息:::" + message.toString()); //將訊息裝成json物件 JSONObject msgObj = JSON.parseObject(message); //得到前臺傳過來的訊息接收者id String toId = msgObj.getString("toId"); //得到傳送者標識 String senderId = msgObj.getString("senderId"); //得到訊息內容 String msgContent = msgObj.getString("msgContent"); for (String s : webSocketSet.keySet()) { if (s.equals(toId)) { Session session1 = webSocketSet.get(s); try { //用map構造訊息格式 懶得建立訊息實體類 Map<String, Object> map = new HashMap<>(); map.put("senderId", fromId); map.put("msgContent", msgContent); //由於這裡使用的string的訊息型別所以在這裡轉成json,當然還有其它訊息型別 sendMessage(JSON.toJSON(map).toString(), session1); } catch (IOException e) { e.printStackTrace(); } return; } } } /** * 發生錯誤時呼叫 * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { error.printStackTrace(); } /** * 這個方法與上面幾個方法不一樣。沒有用註解,是根據自己需要新增的方法。作用是傳送訊息 * * @throws java.io.IOException * @param: message要傳送的訊息 session接收者的會話 */ public static void sendMessage(String message, Session session) throws IOException { session.getBasicRemote().sendText(message); } /** * 功能描述:用來返回當前線上的總人數 * * @param: * @return: * @auther: Administrator * @date: 2018/7/20 9:37 */ public static synchronized int getOnlineCount() { return onlineCount; } /** * 功能描述:線上總人數加一 * * @param: * @return: * @auther: Administrator * @date: 2018/7/20 9:37 */ public static synchronized void addOnlineCount() { WebSocketCt.onlineCount++; } /** * 功能描述:線上總人數減一 * * @param: * @return: * @auther: Administrator * @date: 2018/7/20 9:38 */ public static synchronized void subOnlineCount() { WebSocketCt.onlineCount--; } }
前端程式碼
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <title>簡易實現點對點聊天</title> <body> <div> <input id="connectId" type="text"> <button type="button" id="connect">連線到聊天室</button> </div> <div id="msgInfo"> </div> <div style="margin-top: 50px"> <div> <div><span>接收者id:</span><input type="text" id="toId"> <span>要傳送的訊息:</span><input type="text" id="msg"></input> <button id="send" type="button">傳送訊息</button> </div> </div> </div> </body> <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script> <script> var websocket = null; var conect = null; //點選連線 $("#connect").click(function () { //判斷當前連線是否為空 if (websocket != null) { alert("您已經連線了,請不要重複連線!"); return; } //自定義的唯一標識,如果兩個人輸入的連線標識相同則會覆蓋原來這個標識對應的連線,這裡只是為了簡單演示 var connectId = $("#connectId").val(); if (connectId == null || connectId == '') { alert("請先輸入您的連線標識!"); return } //開始連線 // 首先判斷是否 支援 WebSocket if ('WebSocket' in window) { //路徑ws + ip + port + 自定義路徑 conect = connectId; websocket = new WebSocket("ws://192.168.10.20:8080//websocket/" + connectId); } else { alert("瀏覽器不支援連線!") return; } // 開啟時 websocket.onopen = function (evnt) { console.log(" websocket.onopen "); alert("連線成功!"); }; // 處理訊息時 websocket.onmessage = function (evnt) { //將訊息轉成json格式 var msg = JSON.parse(evnt.data); $("#msgInfo").append("<p>收到--" + msg.senderId + "--給您發訊息:<font color='red'>" + msg.msgContent + "</font></p>"); console.log(" websocket.onmessage "); }; websocket.onerror = function (evnt) { websocket == null; console.log(" websocket.onerror "); }; websocket.onclose = function (evnt) { console.log(" websocket.onclose "); websocket.close(); alert("連線關閉!"); }; }); //傳送訊息 $("#send").click(function () { //先判斷是否連線 if (websocket == null) { alert("您還沒有連線!!!"); return; } //接收者id var toId = $("#toId").val(); //傳送的訊息內容 var msg = $("#msg").val(); if (toId == null || toId == '') { alert("請先輸入接收者"); return; } if (msg == null || msg == '') { alert("訊息不能為空!!!"); return; } //傳送訊息 //構造訊息的json格式 var msgJson = { senderId: conect, msgContent: msg, toId: toId }; websocket.send(JSON.stringify(msgJson)); }); </script> </html>
執行效果截圖