專案結構:

效果展示:

實作步驟
步驟一:添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- json 回傳資料需要 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.54</version>
</dependency>
<!-- 熱部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- websocket 在線聊天支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
步驟二:Java 代碼
1,配置類:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExportelr() {
return new ServerEndpointExporter();
}
}
2,控制層
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author ylwang
* @version 1.0
* @date 2021/8/15 0015 15:23
*/
@ServerEndpoint("/websocket/{username}")
@Controller
public class WebsocketController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
//在線人數
private static int onlineNumber = 0;
private static Map<String, WebsocketController> clients =
new ConcurrentHashMap<>();
//會話
private Session session;
//用戶名稱
private String username;
/**
* 進入聊天室 --> 專案中讀取用戶資訊獲取用戶名
*/
@RequestMapping("/websocket")
public String webSocket(Model model) {
//定義隨機時間戳名稱
String name = "游客:";
String dataName = new SimpleDateFormat("yyyyMMddHHmmsss").format(new Date());
name = name + dataName;
//websocket鏈接地址 + 游客名 ----> 專案種請定義在組態檔 ---> 或直接讀取服務器,ip 埠
String path = "ws://127.0.0.1:8080/websocket/";
//講用戶、地址資訊放在域中
model.addAttribute("path", path);
model.addAttribute("username", name);
//回傳到html頁面
return "socket";
}
/**
* 監聽連接(有用戶連接,立馬到來執行這個方法)
* session 發生變化
*
* @param username
* @param session
*/
@OnOpen
public void onOpen(@PathParam("username") String username, Session session) {
onlineNumber++;
//把新用戶賦給變數
this.username = username;
//把新用戶的session資訊賦給變數
this.session = session;
//輸出websocket資訊
logger.info("現在來連接的客戶id:" + session.getId() + "用戶名:" + username);
logger.info("有新連接加入! 當前在線人數" + onlineNumber);
try {
//把自己的資訊加入到map當中,this = 當前類(把當前類作為物件保存起來)
clients.put(username, this);
//獲得所有的用戶
Set<String> userLists = clients.keySet();
//先給所有用戶發送通知,上線了
//messageType 1代表上線 2代表下線 3代表在線名單 4代表普通訊息
HashMap<String, Object> map1 = new HashMap<>();
// 把所有用戶串列
map1.put("onlineUsers", userLists);
//回傳所有上線狀態
map1.put("messageType", 1);
//回傳用戶名
map1.put("username", username);
//回傳在線人數
map1.put("number", onlineNumber);
//發送全體資訊,(用戶上線資訊)
sendMessageAll(JSON.toJSONString(map1), username);
//給自己發一條訊息,告訴自己現在都有誰在線
HashMap<String, Object> map2 = new HashMap<>();
//messageType 1代表上線 2代表下線 3代表在線名單 4代表普通訊息
map2.put("messageType", 3);
//把所有用戶放入map2
map2.put("onlineUsers", userLists);
//回傳在線人數
map2.put("number", onlineNumber);
//訊息發送指定人(所用的在線用戶資訊)
sendMessageAll(JSON.toJSONString(map2), username);
} catch (Exception e) {
logger.info(username + "上線的時候通知所有人發生了錯誤");
}
}
/**
* 監聽連接斷開(有用戶退出,會立馬到來執行這個方法)
*/
@OnClose
public void onClose(){
onlineNumber--;
//所有在線用戶中去除下線用戶
clients.remove(username);
try {
//messageType 1代表上線 2代表下線 3代表在線名單 4代表普通訊息
Map<String, Object> map1 = new HashMap();
map1.put("messageType", 2);
//所有在線用戶
map1.put("onlineUsers", clients.keySet());
//下線用戶的用戶名
map1.put("username", username);
//回傳在線人數
map1.put("number", onlineNumber);
//發送資訊,所有人,通知誰下線了
sendMessageAll(JSON.toJSONString(map1), username);
}catch (Exception e){
logger.info(username + "下線的時候通知所有人發生了錯誤");
}
logger.info("有連接關閉! 當前在線人數" + onlineNumber);
}
@OnError
public void onError(Session session,Throwable error){
logger.info("服務器發生了錯誤" + error.getMessage());
}
/**
* 監聽訊息(收到客戶端的訊息立即執行)
*
* @param message 訊息
* @param session 會話
*/
@OnMessage
public void OnMessage(String message,Session session){
try {
logger.info("來自客戶端訊息:" + message +"客戶端的id是:"+ session.getId());
//用戶發送的資訊
JSONObject jsonObject = JSON.parseObject(message);
//發送的內容
String textMessage = jsonObject.getString("message");
//發送人
String fromUserName = jsonObject.getString("username");
//接收人 to=all 發送訊息給所有人 || to= !all to == 用戶名
String toUserName = jsonObject.getString("to");
//發送訊息 -- messageType 1代表上線 2代表下線 3代表在線名單 4代表訊息
HashMap<String, Object> map1 = new HashMap<>();
map1.put("messageType",4);
map1.put("textMessage", textMessage);
map1.put("fromusername", fromUserName);
if (toUserName.equals("All")){
//訊息發送所有人(同步)
map1.put("toUserName","所有人");
sendMessageAll(JSON.toJSONString(map1),fromUserName);
}else {
//訊息發送指定人(同步)
map1.put("toUserName",toUserName);
sendMessageTo(JSON.toJSONString(map1), toUserName);
}
}catch (Exception e){
logger.info("發生了錯誤了");
}
}
/**
* 訊息發送指定人
*/
private void sendMessageTo(String message, String toUserName) throws IOException {
//遍歷所有用戶
for (WebsocketController item : clients.values()) {
if (item.username.equals(toUserName)){
//訊息發送指定人(同步)
item.session.getBasicRemote().sendText(message);
break;
}
}
}
/**
* 訊息發送所有人
*/
private void sendMessageAll(String message, String username) throws IOException {
for (WebsocketController item : clients.values()) {
//訊息發送所有人(同步)getAsyncRemote
item.session.getBasicRemote().sendText(message);
}
}
//在線人數
public static synchronized int getOnlineCount(){
return onlineNumber;
}
}
如果在配置類中寫path:

url:
path: ws://127.0.0.1:8080/websocket/
控制層:
@Value("${url.path}")
private String path;
方便實作的話,可以直接寫在控制層,不過建議寫在組態檔中
步驟三:socket頁面
<!DOCTYPE html>
<html xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" href="../frame/layui/css/layui.css">
<link rel="stylesheet" href="../frame/static/css/style.css">
<link rel="icon" href="../frame/static/image/code.png">
<title>websocket</title>
<script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.min.js"></script>
<script src="http://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
</head>
<body>
<!-- socket url -->
<input type="hidden" th:value="${path}" id="path" style="display: none"/>
<!-- 用戶名 -->
<input type="hidden" th:value="${username}" id="username" style="display: none"/>
<br>
<!--<input type="hidden" value="所有人" id="onLineUser" text="所有人" style="display: none" />-->
<!-- overflow-y :auto;overflow :auto; 寬高自適應滾動條-->
<span id="miqx"
style="width:80%;height:300px; background-color: papayawhip;float:left;overflow-y :auto;overflow :auto;">
<li style="text-align: center">群聊資訊</li>
</span>
<span id="miax" style="width:20%;background-color: #24eb04;float:left;overflow-y :auto;overflow :auto;">
<li style="text-align: center">在線串列</li>
</span>
<textarea id="text" placeholder="請輸入內容-發送訊息[Ctrl+回車鍵]" rows="3%" cols="60%"></textarea>
<input onclick="send()" type="button" value="發送">
<td>訊息發送至:</td>
<select id="onLineUser" size="1" style="width: 10%;height:30px">
<option value="所有人">所有人</option>
</select>
<div id="mizx" style="width:80%;height:300px;background-color: #bbebc7;float:left;overflow-y :auto;overflow :auto;">
<li style="text-align: center">私聊資訊</li>
<!-- <li style="text-align: right">靠右</li>
<li style="text-align: left" >靠左</li>-->
</div>
<br>
<br>
</body>
<script type="text/javascript">
function uaername(name) {
alert(name)
}
var miqx = $("#miqx"); //群聊
var miax = $("#miax"); //在線串列
var mizx = $("#mizx"); //私聊
var onLineUser = $("#onLineUser"); //發送人select選擇框
var webSocket;
var commWebSocket;
http:
if ("WebSocket" in window) {
//192.168.100.7:8080/
webSocket = new WebSocket(document.getElementById('path').value +
document.getElementById('username').value);
//連通之后的回呼事件
webSocket.onopen = function () {
miqx.html(miqx.html() +
" <li style='text-align: center'>系統訊息:[登陸成功]</li>")
};
//接收后臺服務端的訊息
webSocket.onmessage = function (evt) {
var received_msg = evt.data; //接收到的資料
var obj = JSON.parse(received_msg); //json資料
var messageType = obj.messageType; //資料型別(1上線/2下線/3在線名單/4發資訊)
var onlineName = obj.username; //用戶名
var number = obj.number; //在線人數
//上線通知+在線串列重繪
if (obj.messageType == 1) {
if ((onlineName != $("#username").val())) { //展示除不等于自己的所有用戶
miqx.html(miqx.html() + " <li style='text-align: center'>系統訊息:[" + onlineName + "]上線了" + "</li>");
onLineUser.html(onLineUser.html() + "<option value='" + onlineName + "'>" + onlineName + "</option>");
}
var onlineName = obj.onlineUsers; //所有在線用戶
miax.html("<li style='text-align: center'>在線用戶--[" + onlineName.length + "]</li>");
for (var i = 0; i < onlineName.length; i++) {
if ((onlineName[i] != $("#username").val())) { //展示除不等于自己的所有用戶
miax.html(miax.html() + "<li style='text-align: left'>---" + onlineName[i] + "</li>");
}
}
//miax.html(miax.html()+" <li style='text-align: center'>"+ onlineName +"</li>");
}
//下線通知+在線串列重繪
else if (obj.messageType == 2) {
if ((onlineName != $("#username").val())) { //展示除不等于自己的所有用戶
miqx.html(miqx.html() + " <li style='text-align: center'>系統訊息:[" + onlineName + "]下線了" + "</li>");
}
var onlineName = obj.onlineUsers; //剩余所有在線用戶
miax.html("<li style='text-align: center'>在線用戶--[" + onlineName.length + "]</li>");
onLineUser.html("<option value='所有人'>所有人</option>");
for (var i = 0; i < onlineName.length; i++) {
if ((onlineName[i] != $("#username").val())) { //展示除不等于自己的所有用戶
miax.html(miax.html() + "<li style='text-align: left'>---" + onlineName[i] + "</li>");
onLineUser.html(onLineUser.html() + "<option value='" + onlineName[i] + "'>" + onlineName[i] + "</option>");
}
}
}
//在線串列
else if (obj.messageType == 3) {
var onlineName = obj.onlineUsers; //所有在線用戶
miax.html("<li style='text-align: center'>在線用戶--[" + onlineName.length + "]</li>");
onLineUser.html("<option value='所有人'>所有人</option>");
for (var i = 0; i < onlineName.length; i++) {
if (onlineName[i] != $("#username").val()) { //展示除不等于自己的所有用戶
miax.html(miax.html() + " <li style='text-align: left'>---" + onlineName[i] + "</li>");
onLineUser.html(onLineUser.html() + "<option value='" + onlineName[i] + "'>" + onlineName[i] + "</option>");
}
}
}
//資訊接收
else {
var time2 = new Date();
var date = time2.getHours() + ":" + time2.getMinutes() + ":" + time2.getSeconds(); //時間
if (obj.fromusername != $("#username").val()) { //自己不接自己的訊息
if (obj.tousername == "所有人") {
//發給所有人
miqx.html(miqx.html() + " <li style='text-align: left'>[" + obj.fromusername + "]說:-" + obj.textMessage + "</li>");
} else {
//發給指定人
mizx.html(mizx.html() + " <li style='text-align: left'>[" + obj.fromusername + "]說:-" + obj.textMessage + "</li>");
}
}
//setMessageInnerHTML(obj.fromusername+"對"+obj.tousername+"說:"+obj.textMessage);
}
};
//連接關閉的回呼事件
webSocket.onclose = function () {
console.log("連接已關閉...");
setMessageInnerHTML("連接已經關閉....");
};
} else {
// 瀏覽器不支持 WebSocket
alert("您的瀏覽器不支持 WebSocket!");
}
//將訊息顯示在網頁上
function setMessageInnerHTML(innerHTML) {
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
function closeWebSocket() {
//直接關閉websocket的連接
webSocket.close();
}
//資訊發送+ 頁面顯示發送資訊
$(document).keyup(function (event) {
//瀏覽器適應
if (event.ctrlKey && event.which == 13 || event.which == 10) {
send();
} else if (event.shiftKey && event.which == 13 || event.which == 10) {
send();
}
});
//資訊發送+ 頁面顯示發送資訊
function send() {
var usernameX = $("#username").val() //發送資料人
var usernameY = $("#onLineUser").val(); //接收資料人
var message = $("#text").val(); //發送的資料
if (usernameY == "所有人") {
usernameY = "All";
/* <li style="text-align: center">群聊資訊</li>
<li style="text-align: right">靠右</li>
<li style="text-align: left" >靠左</li>*/
miqx.html(miqx.html() + " <li style='text-align: right'>" + message + " -- [" + usernameX + "]</li>");
} else {
mizx.html(mizx.html() + " <li style='text-align: right'>" + "你對-[" + usernameY + "]說:-" + message + "</li>");
}
var message = {
"message": message,
"username": usernameX,
"to": usernameY
};
//發送資料
webSocket.send(JSON.stringify(message));
$("#text").val("");
}
layui.use(['form', 'layedit', 'laydate'], function () {
var form = layui.form
, layer = layui.layer
, layedit = layui.layedit
, laydate = layui.laydate;
//監聽指定開關
form.on('switch(switchTest)', function (data) {
layer.msg('你以' + (this.checked ? '上線' : '下線'), {
offset: '6px'
});
//layer.tips('溫馨提示:請注意開關狀態的文字可以隨意定義,而不僅僅是ON|OFF', data.othis)
});
});
</script>
</html>
整天感徑訓是挺簡單的,不要只看不做,最好的學習方式就是自己實作一下,動起手來吧!!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/294433.html
標籤:其他
上一篇:nginx啟動提示nginx: [emerg] bind() to 0.0.0.0:8080 failed (98: Address already in use)
下一篇:vue3 electron 記錄
