SpringBoot集成WebSocket【基于纯H5】进行点对点[一对一]和广播[一对多]实时推送
之前实现websocket基于stomp的,觉得springboot封装的太高,不怎么灵活,现在实现一个纯h5的,也大概了解websocket在内部是怎么传输的。
1.环境搭建
因为在上一篇基于stomp协议实现的websocket里已经有大概介绍过web的基本情况了,所以在这篇就不多说了,我们直接进入正题吧,在springboot中,我们还是需要导入websocket的包。
在pox.xml加上对springboot对websocket的支持:
<!-- websocket --> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-websocket</artifactid> </dependency>
这里大概说一下自己的一点小见解:客户端与服务器建立websocket连接,实际上是创建了一个socket,这个socket是共享与客户端和服务器的。两者只要往对应的socket里操作,就可以实现双方实时通讯了
2.编码实现
一、在springboot中,添加websocket的配置
package com.cloud.sbjm.configure; import org.springframework.context.annotation.configuration; import org.springframework.web.socket.config.annotation.enablewebsocket; import org.springframework.web.socket.config.annotation.websocketconfigurer; import org.springframework.web.socket.config.annotation.websockethandlerregistry; import com.cloud.sbjm.security.websocketinterceptor; import com.cloud.sbjm.service.imp.myhandler; //实现接口来配置websocket请求的路径和拦截器。 @configuration @enablewebsocket public class websocketh5config implements websocketconfigurer{ @override public void registerwebsockethandlers(websockethandlerregistry registry) { //handler是websocket的核心,配置入口 registry.addhandler(new myhandler(), "/myhandler/{id}").setallowedorigins("*").addinterceptors(new websocketinterceptor()); } }
1.@configuration:注解标识该类为spring的配置类
2.@enablewebsocket:开启注解接收和发送消息
3.实现websocketconfigurer接口,重写registerwebsockethandlers方法,这是一个核心实现方法,配置websocket入口,允许访问的域、注册handler、定义拦截器。客户端通过“/myhandler/{id}”直接访问handler核心类,进行socket的连接、接收、发送等操作,这里由于还加了个拦截器,所以建立新的socket访问时,都先进来拦截器再进去handler类,“new websocketinterceptor()”是我实现的拦截器,“new myhandler()”是我实现的一个handler类。
二、websocketinterceptor拦截器的实现:
package com.cloud.sbjm.security; import java.util.map; import javax.servlet.http.httpsession; import org.springframework.http.server.serverhttprequest; import org.springframework.http.server.serverhttpresponse; import org.springframework.http.server.servletserverhttprequest; import org.springframework.web.socket.websockethandler; import org.springframework.web.socket.server.handshakeinterceptor; public class websocketinterceptor implements handshakeinterceptor { //进入hander之前的拦截 @override public boolean beforehandshake(serverhttprequest request, serverhttpresponse serverhttpresponse, websockethandler websockethandler, map<string, object> map) throws exception { if (request instanceof servletserverhttprequest) { string id = request.geturi().tostring().split("id=")[1]; system.out.println("当前session的id="+id); servletserverhttprequest serverhttprequest = (servletserverhttprequest) request; httpsession session = serverhttprequest.getservletrequest().getsession(); map.put("websocket_userid",id); } return true; } @override public void afterhandshake(serverhttprequest serverhttprequest, serverhttpresponse serverhttpresponse, websockethandler websockethandler, exception e) { system.out.println("进来websocket的afterhandshake拦截器!"); } }
1.实现了handshakeinterceptor 接口,并实现了beforehandshake该方法,该方法是在进入handler核心类之前进行拦截。
这里主要实现的逻辑是:
截取客户端建立websocket连接时发送的url地址字符串,并通过对该字符串进行特殊标识截取操作,获取客户端发送的唯一标识(由自己定义的,一般是系统用户id唯一标识,用以标识该用户),并把它以键值对的形式放到session里,这样后期可以通过该session获取它对应的用户id了。【一个session对应着一个websocketsession】
三、myhandler核心类的实现
package com.cloud.sbjm.service.imp; import java.io.ioexception; import java.util.hashmap; import java.util.map; import java.util.set; import net.sf.json.jsonobject; import org.springframework.stereotype.service; import org.springframework.web.socket.closestatus; import org.springframework.web.socket.textmessage; import org.springframework.web.socket.websockethandler; import org.springframework.web.socket.websocketmessage; import org.springframework.web.socket.websocketsession; @service public class myhandler implements websockethandler { //在线用户列表 private static final map<string, websocketsession> users; static { users = new hashmap<>(); } //新增socket @override public void afterconnectionestablished(websocketsession session) throws exception { system.out.println("成功建立连接"); string id = session.geturi().tostring().split("id=")[1]; system.out.println(id); if (id != null) { users.put(id, session); session.sendmessage(new textmessage("成功建立socket连接")); system.out.println(id); system.out.println(session); } system.out.println("当前在线人数:"+users.size()); } //接收socket信息 @override public void handlemessage(websocketsession websocketsession, websocketmessage<?> websocketmessage) throws exception { try{ jsonobject jsonobject = jsonobject.fromobject(websocketmessage.getpayload()); system.out.println(jsonobject.get("id")); system.out.println(jsonobject.get("message")+":来自"+(string)websocketsession.getattributes().get("websocket_userid")+"的消息"); sendmessagetouser(jsonobject.get("id")+"",new textmessage("服务器收到了,hello!")); }catch(exception e){ e.printstacktrace(); } } /** * 发送信息给指定用户 * @param clientid * @param message * @return */ public boolean sendmessagetouser(string clientid, textmessage message) { if (users.get(clientid) == null) return false; websocketsession session = users.get(clientid); system.out.println("sendmessage:" + session); if (!session.isopen()) return false; try { session.sendmessage(message); } catch (ioexception e) { e.printstacktrace(); return false; } return true; } /** * 广播信息 * @param message * @return */ public boolean sendmessagetoallusers(textmessage message) { boolean allsendsuccess = true; set<string> clientids = users.keyset(); websocketsession session = null; for (string clientid : clientids) { try { session = users.get(clientid); if (session.isopen()) { session.sendmessage(message); } } catch (ioexception e) { e.printstacktrace(); allsendsuccess = false; } } return allsendsuccess; } @override public void handletransporterror(websocketsession session, throwable exception) throws exception { if (session.isopen()) { session.close(); } system.out.println("连接出错"); users.remove(getclientid(session)); } @override public void afterconnectionclosed(websocketsession session, closestatus status) throws exception { system.out.println("连接已关闭:" + status); users.remove(getclientid(session)); } @override public boolean supportspartialmessages() { return false; } /** * 获取用户标识 * @param session * @return */ private integer getclientid(websocketsession session) { try { integer clientid = (integer) session.getattributes().get("websocket_userid"); return clientid; } catch (exception e) { return null; } } }
1.实现了websockethandler接口,并实现了关键的几个方法。
① afterconnectionestablished(接口提供的):建立新的socket连接后回调的方法。主要逻辑是:将成功建立连接的websocketsssion放到定义好的常量[private static final map<string, websocketsession> users;]中去。这里也截取客户端访问的url的字符串,拿到标识,以键值对的形式讲每一个websocketsession存到users里,以记录每个socket。
② handlemessage(接口提供的):接收客户端发送的socket。主要逻辑是:获取客户端发送的信息。这里之所以可以获取本次socket的id,是因为客户端在第一次进行连接时,拦截器进行拦截后,设置好id,这样也说明,双方在相互通讯的时候,只是对第一次建立好的socket持续进行操作。
③ sendmessagetouser(自己定义的):发送给指定用户信息。主要逻辑是:根据用户id从常量users(记录每一个socket)中,获取socket,往该socket里发送消息,只要客户端还在线,就能收到该消息。
④sendmessagetoallusers (自己定义的):这个广播消息,发送信息给所有socket。主要逻辑是:跟③类型,只不过是遍历整个users获取每一个socket,给每一个socket发送消息即可完广播发送
⑤handletransporterror(接口提供的):连接出错时,回调的方法。主要逻辑是:一旦有连接出错的socket,就从users里进行移除,有提供该socket的参数,可直接获取id,进行移除。这个在客户端没有正常关闭连接时,会进来,所以在开发客户端时,记得关闭连接
⑥afterconnectionclosed(接口提供的):连接关闭时,回调的方法。主要逻辑:一旦客户端/服务器主动关闭连接时,将个socket从users里移除,有提供该socket的参数,可直接获取id,进行移除。
后台的开发就开发完了,大家有没有发现比基于stomp协议实现要灵活得多?
四、客户端页面的实现【基于h5】
不需要加入任何的js包
<!doctype html> <html> <head> <title>socket.html</title> <meta name="keywords" content="keyword1,keyword2,keyword3"> <meta name="description" content="this is my page"> <meta name="content-type" content="text/html" charset="utf-8"> <!--<link rel="stylesheet" type="text/css" href="./styles.css" rel="external nofollow" >--> </head> <body> welcome<br/> <input id="text" type="text" /><button onclick="send()">send</button> <button onclick="closewebsocket()">close</button> <div id="message"> </div> <!-- 公共js --> <script type="text/javascript" src="../websocket/jquery.min.js"></script> <script type="text/javascript"> var userid="888"; var websocket=null; $(function() { //创建websocket connectwebsocket(); }) //强制关闭浏览器 调用websocket.close(),进行正常关闭 window.onunload = function() { //关闭连接 closewebsocket(); } //建立websocket连接 function connectwebsocket(){ console.log("开始..."); //建立websocket连接 websocket = new websocket("ws://127.0.0.1:9091/cloud-sbjm/myhandler/id="+userid); //打开websokcet连接时,回调该函数 websocket.onopen = function () { console.log("onpen"); } //关闭websocket连接时,回调该函数 websocket.onclose = function () { //关闭连接 console.log("onclose"); } //接收信息 websocket.onmessage = function (msg) { console.log(msg.data); } } //发送消息 function send(){ var postvalue={}; postvalue.id=userid; postvalue.message=$("#text").val(); websocket.send(json.stringify(postvalue)); } //关闭连接 function closewebsocket(){ if(websocket != null) { websocket.close(); } } </script> </body> </html>
页面比较简单,简单解释一下:
1.new websocket("ws://127.0.0.1:9091/cloud-sbjm/myhandler/id="+userid),与服务器建立websocket连接,后面的id="+userid,是动态参数,跟服务器配置handler的访问地址时对应"/myhandler/{id}"。
2.h5也提供多个回调函数
onopen:打开websokcet连接时,回调该函数
onclose:关闭websocket连接时,回调该函数
onmessage:服务器给该socket发送消息时,回调该函数,获取消息
websocket.send(json.stringify(postvalue));:给socket发送消息,服务器获取
websocket.close();客户端主要关闭连接,会触发客户端的onclose方法和服务器的afterconnectionclosed方法
到此服务端的开发也完成了,下面执行一下程序效果图:
一、建立连接
客户端:
服务器:
二、发送消息
客户端:
服务器:
三、服务器主动推送消息
服务器代码:
到此已经完成了,各位可以根据自己需求进行修改,这会灵活多了!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 线程安全的单例模式的几种实现方法分享
下一篇: Java中&&与?表达式结合时出现的坑