🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 一、概述 WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输; 可用于在线客服、在线推送、在线聊天; Tomcat、Netty等服务器及所有主流浏览器都已经支持websocket协议,但由于 Tomcat 的吞吐量、连接数都很低,作为测试是可以的。在生产环境,一定需要使用高吞吐量、高连接数的 Netty 服务器进行替代; ### **与HTTP的区别** ![](https://img.kancloud.cn/64/c7/64c773f44b839317998b3e0188691ef9_628x511.png) ## 二、引入步骤 ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> ``` ## 三、后端开发 ### **参数配置** ``` rayframework.enabled.websocket=true ``` ### **后端开发** 服务器端开发,实质上,就是发布一个个(当然也可以仅仅发布一个)的服务地址,供前端建立连接,进行通讯;平台已经做好了通讯的基础设施处理,业务开发中,只需要调用平台提供的api,对指定在线用户进行信息发送即可; 后端提供了两类服务场景,供业务来调用, 一种是作为当前用户作为服务端,发送信息给客户端: ``` #发送信息给指定在线账户 /api/system/utility/rayim/sender/spec #发送信息给所有在线账户 /api/system/utility/rayim/sender/all ``` 另外一种是设定服务端账户,平台支持将所有的客户信息转发给多个服务端,通过本接口来设定接受信息的服务端账户; ``` #自动将自己注册作为服务端 /api/system/utility/rayim/waiter/register/auto #将指定账户从服务端删除 /api/system/utility/rayim/waiter/register/remove #显示当前所有服务端 ``` 内部调用: ``` @Autowired private RayWebsocketSender rayWebsocketSender; ``` ## 四、前端开发 ### **服务地址** 服务调用地址: ``` ws://ip:port/rayim?rayAccessToken=${rayAccessToken} ``` >[danger] > 1、 ip为当前应用部署的地址,port为当前应用部署的端口; > 2、${rayAccessToken}是当前账号的的rayAccessToken,登录后就能从主程序中获取得到,这里就包含了用来识别当前用户的staffId信息; > 3、如果你的账户被注册为服务端了,则所有的客户端信息,你都能接受得到; ### **代码参考** 客户端直接用js,参考代码: ``` <html> <body> <input type="text" id="msg" size="100" value="123"> <button onclick="send()">发送消息</button> <script type="text/javascript"> var ws = new WebSocket('ws://www.rayframework.com/raysale/rayim?rayAccessToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIiwic3ViIjoidW5pZnJhbWV3b3JrIiwic3RhZmZDb2RlIjoic3VwZXJtYW5hZ2VyIiwiY29ycG9yYXRpb25JZCI6MCwiaWRlbnRpdHlJZCI6MywiZGVwYXJ0bWVudElkIjoxLCJmcm9tSG9zdCI6IjExMy4xMTguMTg0LjEyNSIsImV4cCI6MTYzMjMxMjg0NCwiaWF0IjoxNjMyMzA5MjQ0LCJzdGFmZklkIjoxLCJkb21haW5JZCI6MX0.xKEYCCJRZApNcGOqktihU-ysQMGjrwu1Cqm8q0LvjD4'); ws.onopen = function() { console.log("open"); }; ws.onclose = function() { console.log("websocket已关闭"); }; ws.onerror = function() { console.log("websocket发生了错误"); } ws.onmessage = function(msg) { document.getElementById("msg").value=msg.data; }; function send() { var txt= document.getElementById("msg").value; ws.send(txt); } </script> </body> </html> ``` 进阶版本,支持图片: ``` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>websocket测试</title> </head> <body> <input type="text" id="text"><button onclick="sendText()">发送</button> <input type="file" id="f" onchange="chooseFile()"> <div id="main"> </div> <script type="text/javascript"> var ws = new WebSocket('ws://www.rayframework.com/raysale/rayim?rayAccessToken=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIiwic3ViIjoidW5pZnJhbWV3b3JrIiwic3RhZmZDb2RlIjoic3VwZXJtYW5hZ2VyIiwiY29ycG9yYXRpb25JZCI6MCwiaWRlbnRpdHlJZCI6MywiZGVwYXJ0bWVudElkIjoxLCJmcm9tSG9zdCI6IjExMy4xMTguMTg0LjEyNSIsImV4cCI6MTYzMjMxMjg0NCwiaWF0IjoxNjMyMzA5MjQ0LCJzdGFmZklkIjoxLCJkb21haW5JZCI6MX0.xKEYCCJRZApNcGOqktihU-ysQMGjrwu1Cqm8q0LvjD4'); ws.onopen = function (ev) { console.log("------连接服务器成功-----") } ws.onerror=function (ev) { console.log("------连接服务器出错-----") } //接收消息 ws.onmessage=function (ev) { //解析json var json = JSON.parse(ev.data); //1为文本消息 if(json.code==1){ document.querySelector("#main").innerHTML="<p>"+json.msg+"</p>"+document.querySelector("#main").innerHTML; //2为图片消息 }else if(json.code==2){ document.querySelector("#main").innerHTML='<img width="150px" src='+json.msg+'>'+document.querySelector("#main").innerHTML; } } //发送文本消息 function sendText() { var msg = document.querySelector("#text").value if(msg){ ws.send(JSON.stringify({code:1,msg:msg})); document.querySelector("#text").value="" } } //发送图片消息 function chooseFile() { var files = document.querySelector("#f").files if(files.length>0){ var fileReader = new FileReader(); fileReader.readAsDataURL(files[0]) fileReader.onload=function (e) { var s = JSON.stringify({code:2,msg:e.target.result}); ws.send(s) } } } </script> </body> </html> ``` ### **心跳** 后端提供了心跳保持机制,前端需定时发送`rayHeartBeat`心跳,后端发回`rayImHeartBeatFeedback`反馈; > 1、可与发送信息合并处理,发送完信息,开始计时,指定时间后,若还未发送信息,则发送心跳,然后重新计时,只要发送信息或发送心跳,都会重置计时器; > 2、超时断连接时间,这个跟服务器有关,所以,治本的方法是用心跳保持来做; > 3、前端注意,心跳反馈信息是没有业务意涵的,需考虑显示的时候过滤;