引入对应maven依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> 复制代码
注入ServerEndpointExporter
@Configuration public class WebSocketConfig { /** * 服务器节点 * * 如果使用独立的servlet容器,而不是直接使用springboot的内置容器,就不要注入ServerEndpointExporter,因为它将由容器自己提供和管理 * @return */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } 复制代码
代码实现webSocket,具体的实现还要看业务需求,GuavaCacheUtil是我本地的一个guava的缓存工具类
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import io.renren.common.exception.RRException; import io.renren.common.utils.GuavaCacheUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; /** * @Auther zxl * @Date 2020/3/8 16:49 * @Description **/ @ServerEndpoint("/websocket/{sToken}") @Component @Slf4j public class WebSocketServer { private static ConcurrentHashMap<String,WebSocketServer> webSocketMap = new ConcurrentHashMap<>(); /** * 管理员聊天用户数目 */ private static ConcurrentHashMap<String,Integer> adminUserCountMap = new ConcurrentHashMap<>(); /** * 在线的管理员 */ private static AtomicInteger adminCount = new AtomicInteger(0); /** * 在线的用户 */ private static AtomicInteger userCount = new AtomicInteger(0); private Session session; private String sToken; private static final String SYS_STOKEN = "SYS"; /** * 连接建立成功 * @param session * @param sToken */ @OnOpen public void onOpen(Session session,@PathParam("sToken") String sToken){ if (sToken.endsWith("000")){ // 测试使用 GuavaCacheUtil.put(sToken,0); } if(GuavaCacheUtil.getIfPresent(sToken) == null){ // sToken无效或失效 return; } this.session = session; this.sToken = sToken; if (webSocketMap.containsKey(sToken)){ webSocketMap.remove(sToken); } webSocketMap.put(sToken,this); if (sToken.startsWith("ADMIN")){ adminCount.incrementAndGet(); if (adminUserCountMap.containsKey(sToken)){ adminUserCountMap.remove(sToken); } adminUserCountMap.put(sToken,0); }else if (sToken.startsWith("USER")){ userCount.incrementAndGet(); // 分配管理员 String adminSToken = getAdmin(); // 通知用户管理员sToken JSONObject jsonObject = new JSONObject(); jsonObject.put("msg",adminSToken); jsonObject.put("toUser",sToken); jsonObject.put("fromUser",SYS_STOKEN); jsonObject.put("code",1); this.sendMessage(jsonObject.toJSONString()); // 管理员用户数+1 adminUserCountMap.put(adminSToken,adminUserCountMap.get(adminSToken) + 1); } } @OnClose public void onClose(){ if (webSocketMap.containsKey(sToken)){ webSocketMap.remove(sToken); } if (sToken.startsWith("ADMIN")){ adminCount.decrementAndGet(); }else if (sToken.startsWith("USER")){ userCount.decrementAndGet(); } GuavaCacheUtil.remove(sToken); } @OnMessage public void onMessage(String message, Session session){ JSONObject jsonObject = JSON.parseObject(message); String toUser = jsonObject.getString("toUser"); if (StringUtils.isNotBlank(toUser)&&webSocketMap.containsKey(toUser)){ jsonObject.put("fromUser",sToken); jsonObject.put("code",0); webSocketMap.get(toUser).sendMessage(jsonObject.toJSONString()); }else { jsonObject.remove("toUser"); jsonObject.remove("msg"); jsonObject.put("toUser",sToken); jsonObject.put("fromUser",SYS_STOKEN); jsonObject.put("code",404); jsonObject.put("msg","对方已下线"); webSocketMap.get(sToken).sendMessage(jsonObject.toJSONString()); } log.info("sToken: {},message:{}",sToken,jsonObject.toJSONString()); } @OnError public void onError(Session session, Throwable error) { JSONObject jsonObject = new JSONObject(); jsonObject.put("toUser",sToken); jsonObject.put("fromUser",SYS_STOKEN); jsonObject.put("code",500); jsonObject.put("msg","系统异常"); webSocketMap.get(sToken).sendMessage(jsonObject.toJSONString()); log.error("用户错误:"+sToken+",原因:"+error.getMessage()); } /** * 实现服务器主动推送 */ public void sendMessage(String message){ try { this.session.getBasicRemote().sendText(message); } catch (IOException e) { log.error("socket error: {}",e); } log.info("socket send msg : {}",message); } /** * 获取聊天用户最少的管理员sToken * @return */ private String getAdmin(){ if (adminUserCountMap.size() == 0){ return null; } String sToken = ""; int count = 0; Iterator<Map.Entry<String,Integer>> iterator = adminUserCountMap.entrySet().iterator(); while (iterator.hasNext()){ Map.Entry<String,Integer> m = iterator.next(); if (StringUtils.isBlank(sToken)){ sToken = m.getKey(); count = m.getValue(); }else if (m.getValue() < count){ sToken = m.getKey(); count = m.getValue(); } } return sToken; } /** * 自定义发送游戏 * @param msg * @param sToken */ public static void sendMsg(String msg,String sToken){ if (StringUtils.isNotBlank(sToken) && webSocketMap.containsKey(sToken)){ JSONObject jsonObject = new JSONObject(); jsonObject.put("msg",msg); jsonObject.put("toUser",sToken); jsonObject.put("fromUser",SYS_STOKEN); jsonObject.put("code",0); webSocketMap.get(sToken).sendMessage(jsonObject.toJSONString()); }else { throw new RRException("用户已下线"); } }} 复制代码
开放一个http接口用于推送消息,R是我自定义的全局返回对象
/** * @Auther zxl * @Date 2020/3/8 19:11 * @Description **/ @RestController public class ScoketController { @RequestMapping("/push/{sToken}") public R pushToWeb(@RequestParam String message, @PathVariable String sToken) { WebSocketServer.sendMsg(message,sToken); return R.ok(); } } 复制代码
管理员页面
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>管理员scoket</title> </head> <body id="body"> <h1>当前用户:<span id="userCount"></span></h1> </body> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> var socket; var sToken; var user_sToken = [] var userCount = 0 $(function(){ getStoken(); initSocket(); $("#userCount").html(0) }) // 获取sToken function getStoken(){ // 模拟sToken,测试使用管理员sToken(ADMIN开头,000结尾),真实环境掉接口获取sToken sToken = "ADMIN1000" } // 初始化socket function initSocket(){ if(typeof(WebSocket) == "undefined") { console.log("您的浏览器不支持WebSocket"); alert("您的浏览器不支持WebSocket"); }else{ console.log("您的浏览器支持WebSocket"); var socketUrl="http://localhost:8082/bocai-api/websocket/"+sToken; socketUrl=socketUrl.replace("https","ws").replace("http","ws"); console.log(socketUrl); if(socket!=null){ socket.close(); socket=null; } socket = new WebSocket(socketUrl); //打开事件 socket.onopen = function() { console.log("websocket已打开"); alert("socket打开成功") }; //获得消息事件 socket.onmessage = function(msg) { console.log(msg.data); var data = JSON.parse(msg.data) if(data.code != 0){ alert(data.msg) return; } console.log(data) console.log(user_sToken) if($.inArray(data.fromUser,user_sToken) >= 0){ $("#"+data.fromUser).append('<tr><td>'+data.msg+'</td><td><td></tr>') console.log('table添加新记录') }else { // 新用户消息 console.log('创建新table') $("#body").append('<table style="margin-top:10px" border="2" bordercolor="black" width="500" cellspacing="0" cellpadding="5" id='+data.fromUser+'></table>') $("#"+data.fromUser).append('<tr><td>通信用户:'+data.userName+data.mobile+'</td><td>用户sToken:'+data.fromUser+'</td></tr>') $("#"+data.fromUser).append('<tr><td colspan="2"><input id=ip'+data.fromUser+' type="text"><button data-st='+data.fromUser+' onclick="sendMsg(this)">发送</button></td></tr>') $("#"+data.fromUser).append('<tr><td>用户</td><td>我</td></tr>') $("#"+data.fromUser).append('<tr><td>'+data.msg+'</td><td><td></tr>') user_sToken.push(data.fromUser) userCount++ $("#userCount").html(userCount) } }; //关闭事件 socket.onclose = function() { console.log("websocket已关闭"); }; //发生了错误事件 socket.onerror = function() { console.log("websocket发生了错误"); } } } function sendMsg(e){ var user_st = e.getAttribute("data-st") console.log(user_st) console.log($("#ip"+user_st).val()) var data = {msg:$("#ip"+user_st).val(),toUser:user_st,fromUser:sToken} sendMessage(JSON.stringify(data)) $("#"+user_st).append('<tr><td></td><td>'+$("#ip"+user_st).val()+'</td></tr>') } function sendMessage(data) { if(typeof(WebSocket) == "undefined") { console.log("您的浏览器不支持WebSocket"); }else { console.log("您的浏览器支持WebSocket"); console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}'); socket.send(data); } } </script> </html> 复制代码
用户页面
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>用户scoket</title> </head> <body id="body"> <h1>当前用户:<span id="userName"></span></h1> <table border="2" bordercolor="black" width="300" cellspacing="0" cellpadding="5" id="msgTable"> <tr> <td colspan="2"> <input id="msg" type="text"><button onclick="sendMsg()">发送</button> </td> </tr> <tr> <td>客服</td> <td>我</td> </tr> </table> </body> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> var socket; var sToken; var admin_sToken; var userName = '小明' var mobile = '18539442736' $(function(){ getStoken(); initSocket(); $("#userName").html(userName+mobile) }) // 获取sToken function getStoken(){ // 模拟sToken,测试使用管理员sToken(USER开头,000结尾),真实环境掉接口获取sToken sToken = "USER2000" } // 初始化socket function initSocket(){ if(typeof(WebSocket) == "undefined") { console.log("您的浏览器不支持WebSocket"); alert("您的浏览器不支持WebSocket"); }else{ console.log("您的浏览器支持WebSocket"); var socketUrl="http://39.105.129.210:8082/bocai-api/websocket/"+sToken; socketUrl=socketUrl.replace("https","ws").replace("http","ws"); console.log(socketUrl); if(socket!=null){ socket.close(); socket=null; } socket = new WebSocket(socketUrl); //打开事件 socket.onopen = function() { console.log("websocket已打开"); alert("socket打开成功") }; //获得消息事件 socket.onmessage = function(msg) { console.log(msg.data); var data = JSON.parse(msg.data) if(data.code == 1){ admin_sToken = data.msg }else if(data.code == 0){ $("#msgTable").append('<tr><td>'+data.msg+'</td><td></td></tr>') console.log('table添加新记录') }else { alert(data.msg) } }; //关闭事件 socket.onclose = function() { console.log("websocket已关闭"); }; //发生了错误事件 socket.onerror = function() { console.log("websocket发生了错误"); } } } function sendMsg(){ var msg = $("#msg").val() var data = {userName:userName,mobile:mobile,msg:msg,toUser:admin_sToken,fromUser:sToken} sendMessage(JSON.stringify(data)) $("#msgTable").append('<tr><td></td><td>'+msg+'</td></tr>') } function sendMessage(data) { if(typeof(WebSocket) == "undefined") { console.log("您的浏览器不支持WebSocket"); }else { console.log("您的浏览器支持WebSocket"); console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}'); socket.send(data); } } </script> </html> 只是一个简单的demo,期待分享更优质的内容复制代码