使⽤WebSocket解决页⾯数据实时同步
功能:
⼀个页⾯需要在不同的PC端访问,在某⼀PC端对⽹页内容发⽣改变时,其他PC端页⾯数据实时更新显⽰.
实现:
采⽤webSocket+AOP通知的⽅式实现
思路:
当页⾯数据修改时,会通过后端保存⽅法存进数据库,这样我们就要⼀个⼊⼝,当数据保存⽅法被调⽤执⾏完后(AOP后置通知),触发webSocket消息机制,向前端发送更新提⽰,前端调⽤更新⽅法进⾏页⾯更新.
实现过程:
在⽹上有很多关于webSocket的实现和总结,这⾥我就不⼀⼀叙述,⼤家有兴趣可以在⽹上查阅,但是⼤致实现⽅式我总结有三种;
第⼀种:原⽣实现⽅式
第⼆种:spring管理的⽅式
第三种:springBoot管理的⽅式
我采⽤的是第三种,⼤家可以根据⾃⼰项⽬的结构和框架来选择使⽤哪种⽅式,但是原理都是⼀样的,只是代码的写法可能略有不同⽽已.
代码:
前端:
function webSocket() {
var websocket = null;
//判断当前浏览器是否⽀持WebSocket
if (typeof(WebSocket) == "undefined") {
alert('当前浏览器 Not support websocket');
}
else {
//建⽴连接,这⾥的/websocket ,是Servlet中注解中的那个值
console.log("support websocket");
var id =ground.userID;
websocket = new WebSocket("ws://" + window.location.host + "/项⽬名/websocket/"+ id);
}
//连接发⽣错误的回调⽅法
console.log("WebSocket连接发⽣错误");
};
/
/连接成功建⽴的回调⽅法
console.log("WebSocket连接成功");
}
//接收到消息的回调⽅法
console.log("数据更新啦");
loadfg();
}
//连接关闭的回调⽅法
console.log("WebSocket连接关闭");
}
//监听窗⼝关闭事件,当窗⼝关闭时,主动去关闭WebSocket连接,防⽌连接还没断开就关闭窗⼝,server端会抛异常。 beforeunload = function () {
websocket.close();
}
}
后端:核⼼类WebSocketServer⽤于连接前端,完成前后端通信
@Slf4j
@ServerEndpoint(value = "/websocket/{id}", configurator = MyEndpointConfigure.class)
@Controller
public class WebSocketServer {
private Map<String, Session> map;
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
private String id;
/**
* @param session websocket连接sesson
* @DESC <p>注解{@link OnOpen} 声明客户端连接进⼊的⽅法</p>
*/
@OnOpen
public void onOpen(@PathParam("id") String id, Session session) {
log.debug("连接成功");
this.session = session;
this.id = id;
log.debug(this.id);
map = Instance();
map = Instance();
// 将连接session对象存⼊map
map.put(this.id, session);
}
/**
* <p>{@link OnClose} 关闭连接</p>
*/
前端websocket怎么用
@OnClose
public void onClose() {
log.debug("连接关闭---关闭id" + this.id);
}
/**
* <p>{@link OnMessage} 消息监听处理⽅法</p>
*
* @throws IOException 异常
*/
@OnMessage
public void onMessage(String message) {
log.debug(message);
}
/**
* <p>{@link OnError} websocket系统异常处理</p>
*
* @param t 异常
*/
@OnError
public void onError(Throwable t) {
<(t + "连接异常");
t.printStackTrace();
}
public void send(String date) {
log.debug("开始发送消息");
try {
Iterator<String> iter = map.keySet().iterator();
while (iter.hasNext()) {
String key = ();
if ((key).isOpen()) {
if (key.equals(date)) {
<(key).getBasicRemote().JSONString(ids[0])); log.debug("消息发送");
}
} else {
log.debug("已经断开连接的有..." + key);
//删除断开的连接
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
这个⽅法⾥⾯有的做法和⽹上的案例有⼀些细微的不同,因为我在测试的时候发现采⽤⽹上的关闭页⾯调⽤页⾯关闭触发⽅法,在⽅法内删除当前id的⽅式在实际中会有问题,因为他删除的总是最后⼀个访问websocket的页⾯的session,所以导致要么最后⼀个页⾯收不到消息,要么第⼆个⼈关闭页⾯时后台会报错,所以我在页⾯关闭触发⽅法⾥⾯没有做session删除的处理,⽽是在发送消息时做了isOpen()判断,为true时就是session处于连接状态,为false时就是处于断开状态,⽽在false时我做了删除session处理,将断开的session删除.这样就可以保证不会将处于连接状态的session删除
后端:MyEndpointConfigure这个类我没有太多去研究,也是⽹上拷的,但我个⼈理解的话这个类应该是⽤来⽣成webSocket对象的,如果我的理解有问题,各位⼤⽜⼀定⼀定要私信我,拜谢!!
public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware
{
private static volatile BeanFactory context;
@Override
public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException
{
Bean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
}
}
后端:WebSocketConfig类,这个类就很重要了,这个类是⽤来将webSocket注⼊到spring容器中的,如果不使⽤这个类的话,是没⽤办法在webSocket类中去使⽤@Autowired注解应⽤对象的
@Configuration
public class WebSocketConfig {
private MyEndpointConfigure myEndpointConfigure;
@Bean
public MyEndpointConfigure newConfigure()
{
return new MyEndpointConfigure();
}
}
后端:AOP类,这个类就是⼀个AOP后置通知,具体的注解或者aop的通知机制,不清楚的⼩伙伴,可以去相关资料回顾⼀下
@Aspect
@Component
@Slf4j
public class AopLogging {
@Autowired
private WebSocketServer webSocketServer;
@After("execution(*com.it.i.controller.InspectorController.save(..))")
public void after(){
log.debug("后置通知进⼊");
}
}
* 代表该⽅法为所有类型和所有返回值类型,括号⾥的..表⽰该⽅法的所有类型参数
意思就是只要在这个包下的这个类⾥⾯的所有的save⽅法,不管返回值类型和参数类型,只要执⾏这个类⾥⾯名为save的⽅法后置通知⽅法就会被触发,当然如果这个类⾥⾯有很多重载的save⽅法,⽽我们只需要其中某个⽅法去触发后置通知,就不能⽤*和..表⽰了
个⼈项⽬总结:
在实现功能期间,遇到过很多问题,⾸先是前后端连接不通,⼀定要注意加上项⽬名称,并且保证项⽬名称没有问题,还有就是连接地址没有问题⼤⼩写_和-等等,都是细⼼的问题;
遇到的最⼤的问题就是只有⼀个PC端页⾯能接收到更新的提⽰⽽其他页⾯没有反应,通过断点,查看⽇志等⽅式,发现同⼀个页⾯在不同浏览器和PC端打开,连接后端⽣成的webSocket都是同⼀个对象,所以在存储进集合(之前⽤的set)中时后⾯⼀个连接的对象会将前⾯⼀个对象覆盖掉(因为set的不可重复的),后来改⽤map集合,当前时间为键,webSocket对象为值,遍历集合才发现这⼀个问题,⼀直苦苦思索解决办法,也问了很多⼈,但是⼀直没有结果,后来研究代码发现,最终往前端发送消息是⽤webSocket中的session来完成的,突然灵光⼀闪,虽然webSocket是相同的,但是session肯定是不⼀样的(这个原因就不阐述了,相信⼤家都知道),既然最后的是通过session通信的,所以我就将之前的往集合⾥⾯添加webSocket对象改为往集合中添加当前Session对象,由于业务需求,就将⽤户名设置为了key.
结束语:
我也是第⼀次使⽤webSocket,如果有问题的地⽅⼤家可以以前研究探讨,如果我上述说的有问题或⼤家遇到什么问题,请私信我,拜谢!!
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论