websocket是客户端和服务端的通信,所以它肯定有客户端和服务端
这里客户端直接用原生的html5来编写
首先当然是先引入对应的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
然后注入相应的bean
package com.chan.wechatshop.config;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Component
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
js中websocket建立连接后websocket对象的几种状态
可以用websocket.readyState查看
0 :对应常量CONNECTING (numeric value 0),
正在建立连接连接,还没有完成。The connection has not yet been established.
1 :对应常量OPEN (numeric value 1),
连接成功建立,可以进行通信。The WebSocket connection is established and communication is
possible.
2 :对应常量CLOSING (numeric value 2)
连接正在进行关闭握手,即将关闭。The connection is going through the closing handshake.
3 : 对应常量CLOSED (numeric value 3)
连接已经关闭或者根本没有建立。The connection has been closed or could not be opened.
weosocket接收客户端消息,发送消息给客户端实现类
这里要注意,每次建立websocket连接的时候都会新建一个下面路由中的WebSocket class
对象,所以在里面注入spring
的bean
的时候是没有经过spring
的依赖注入也就没有bean
实例的,所以需要调用spring
的bean
的话需要将属性声明成static
然后再注入,或者新增一个工具类,用static
注入.
可以注意到下面demo中的webSockets属性就是static的,就是这个原因
package com.chan.wechatshop.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
@Component
@ServerEndpoint("/websocket")
@Slf4j
public class WebSocket {
private Session session;
//普通的Set存不进
private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();
/**
* 对应前端的相应事件
* @param session
*/
@OnOpen
public void onOpen(Session session){
this.session = session;
webSockets.add(this);
log.info("[websocket消息] 有新的连接,总数:{}",webSockets.size());
}
/**
* 关闭的时候移除
*/
@OnClose
public void onClose(){
webSockets.remove(this);
log.info("[websocket消息] 连接断开,总数:{}",webSockets.size());
}
@OnMessage
public void onMessage(String message){
log.info("[websocket消息] 收到客户端发来的消息:{}",message);
}
public void sendMessage(String message){
for(WebSocket webSocket :webSockets){
log.info("[websocket消息] 广播消息,message={}",message);
try {
webSocket.session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
log.error("[websocket消息] 广播消息,出现异常,{}",e.getMessage());
}
}
}
}
要使用的地方注入进去就可以了,然后调用sendMessage广播消息
前端也就是客户端
js中websocket的4种状态
websocket.readyState
连接服务端地址
前端页面打开的时候自动执行这一段js代码,这时可以在服务端看到已经连上了websocket
客户端也打印输出了一下控制台,也可以看到websocket连接服务端成功
在成功连上服务端后,前端页面刷新可以看到在服务端又重新连接了一次,是因为,前端页面刷新的时候触发了onbeforeunload,理论上来说,页面刷新前进入到了这个onbeforeunload事件后由前端发起关闭,但是实际中我操作的时候不知道为啥,后端没等前端执行webSocket.close();就提前关闭了
整体demo
<script type="text/javascript">
layui.use(['layer'],function () {
var webSocket = null;
var layer = layui.layer
$ = layui.jquery;
if('WebSocket' in window){
webSocket = new WebSocket('ws://felixchan.natapp1.cc/websocket');
}else {
// alert("该浏览器不支持websocket!");
layer.msg("该浏览器不支持websocket!",{icon : 5});
}
webSocket.onopen = function (event) {
console.log('建立连接');
webSocket.send("is test connecttions~");
};
webSocket.onclose = function (event) {
console.log('连接关闭');
};
webSocket.onmessage = function (event) {
console.log('收到消息:' + event.data);
//收到消息之后,可以弹框,播放音乐等
//示范一个公告层
var audio = document.getElementById('getNewOrderNotice');
audio.play();
layer.open({
type: 1
,title: false //不显示标题栏
,closeBtn: false
,area: '300px;'
,shade: 0.8
,id: 'LAY_layuipro' //设定一个id,防止重复弹出
,btn: ['确定','查看新的订单']
,btnAlign: 'c'
,moveType: 1 //拖拽模式,0或者1
,content: '<div style="padding: 50px; line-height: 22px; background-color: #393D49; color: #fff; font-weight: 300;text-align: center;"><h1>提醒</h1>你有新的订单啦</div>'
,success: function(layero){
var btn = layero.find('.layui-layer-btn');
// 点击查看新订单这里就刷新一下页面
btn.find('.layui-layer-btn1')[0].onclick = function () {
location.reload();//当前页面刷新一下
};
}
});
};
webSocket.onerror = function () {
layer.msg("websocket通信发生错误!",{icon : 5});
};
// 在窗口关闭的时候把websocket也关闭掉
window.onbeforeunload = function () {
webSocket.close();
};
})
</script>
有时候可能后端断了,这个时候前端会同时进入websocket.close()事件中,但是接下来后端重启了,前端不去重连是不行的,所以这里要设置一个定时器,去重连websocket
ps:这里注意重连的时候也是要监听open,close,onmessage这些事件的,即使重新连接上了,后端发消息过来前端也收不到,所以这里可以把监听事件封装成一个方法,把new出来的websocket对象传进去
这里举了个coffee的例子,js也是一样的做法
websocketWithConnected: function(websocket) {
var it, me;
it = this.wso;
me = this;
websocket.onopen = function() {
console.log('建立连接');
return it.rfidPackingCheckwebsocket.send("{pageId:" + it.app.workspaceManager.wsoContainer.selectedChildWidget.wso.navigatorItem.id + "}");
};
websocket.onclose = function() {
return console.log('连接关闭');
};
websocket.onmessage = function(event) {
var data, originalEpcCode, resultEpcCode;
console.log('收到消息:' + event.data);
if ('RFIDPacking' === it.app.workspaceManager.wsoContainer.selectedChildWidget.wso.navigatorItem.id) {
if (it.queryCode && it.queryCode.getValue() !== '') {
data = JSON.parse(event.data);
resultEpcCode = data.resultEpcCode;
originalEpcCode = data.originalEpcCode;
it.resultEpcCode.innerHTML = resultEpcCode;
it.originalEpcCode.innerHTML = originalEpcCode;
it.codeInput.set('value', resultEpcCode);
if (!me.checkItem(resultEpcCode)) {
return it.codeInput.set('value', '');
} else {
return me.pack(it, me, resultEpcCode);
}
} else {
return layer.alert("请先输入查询code");
}
}
};
return websocket.onerror = function() {
return layer.msg("websocket通信发生错误!", {
icon: 5
});
};
}