WebSocket通信,多客户端数据同时发送
目标:当一个客户端改变了某项数据后,其他的客户端都可以通过 WebSocket方式 看到新的数据。
单浏览器推送
需要用到的pom坐标:
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0-rc5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
页面:
<div>
<input type="button" value="发送数据" onclick="send('黎明')"/> <input type="button" value="关闭连接" onclick="onClose()"/>
<div id="ht" style="font-size: 15px;"></div>
</div>
<script type="text/javascript">
let webSocket;
if ('WebSocket' in window) {
webSocket = new WebSocket('ws://localhost:8088/你的项目名/coverSocket');
} else if ('MozWebSocket' in window) {
webSocket = new MozWebSocket('ws://localhost:8088/你的项目名/coverSocket');
} else {
console.log('此浏览器暂不支持webSocket');
}
if (webSocket != null) {
webSocket.onopen = function (event) {
onOpen(event)
};
webSocket.onclose = function () {
onClose()
};
webSocket.onerror = function (event) {
onError(event)
};
webSocket.onmessage = function (event) {
onMessage(event)
};
//监听窗口关闭
window.onbeforeunload = function () {
webSocket.close();
};
}
function onOpen(event) {
console.info("WebSocket连接打开" + event);
}
function onClose() {
webSocket.close();
console.info("WebSocket连接关闭");
}
function onError(event) {
console.info("WebSocket发生错误" + event);
}
//接收数据
function onMessage(event) {
$('#ht').append('<br>' + event.data)
}
//发送数据
function send(st) {
return webSocket.send(st);
}
</script>
java类:
import org.springframework.stereotype.Component;
import org.springframework.web.socket.server.standard.SpringConfigurator;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
//configurator = SpringConfigurator.class 通过这个类,该WebSocket中就可以注入Spring容器的bean。
@ServerEndpoint(value = "/coverSocket", configurator = SpringConfigurator.class)
public class WebSocketServer {
//你注入的BeanService
@Autowired
private BeanService beanService;
@OnOpen
public void onOpen(Session session) {
String id = session.getId();
System.err.println("通道打开"+id);
}
@OnClose
public void onClose(Session session) {
String id = session.getId();
System.err.println("通道关闭"+id);
}
@OnError
public void onError(Session session,Throwable error){
System.err.println("WebSocket发生错误");
error.printStackTrace();
}
@OnMessage
public void onMessage(String message, Session session) throws IOException, InterruptedException {
//你业务层操作的方法
MyBean bean = beanService.getByUniqueKey("xx","xx", MyBean .class);
session.getBasicRemote().sendText("欢迎您:"+bean.toString());
}
}
- 这种是最基本的从库里查数据然后推送给一个浏览器,就不解释了。
- 如果要实现从一个浏览器中点击按钮,给多个浏览器推送数据,怎么实现呢?
- 那就需要把浏览器和服务端的通信保存起来(session),每次有数据传输时,都把session取出来,然后发送数据。
- 注意:这个session不是平时保存用户信息的servlet session,而是websocket的session。
多浏览器同步推送
前端需要修改的代码如下:
function myBrowser(){
let userAgent = navigator.userAgent; //取得浏览bai器du的userAgent字符串
let isOpera = userAgent.indexOf("Opera") > -1;
if (isOpera) {
return "Opera"
} //判断zhi是否Opera浏览器
if (userAgent.indexOf("Firefox") > -1) {
return "Firefox";
} //判断是否Firefox浏览器
if (userAgent.indexOf("Chrome") > -1){
return "Chrome";
}
if (userAgent.indexOf("Safari") > -1) {
return "Safari";
} //判断是否Safari浏览器
if (userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1 && !isOpera) {
return "IE";
} //判断是否IE浏览器
}
//需要修改的部分
webSocket = new
WebSocket('ws://localhost:8088/你的项目名/coverSocket/'+myBrowser());
- 需要传一个值用来区分每一个浏览器的标识
- 这里我用了谷歌和火狐两个浏览器测试,就加了一个方法获取当前浏览器的标识,把这个标识传给后台。
后台修改后的代码
//configurator = SpringConfigurator.class 通过这个类,该WebSocket中就可以注入Spring容器的bean。
@ServerEndpoint(value = "/coverSocket/{name}", configurator = SpringConfigurator.class)
@Component
public class WebSocketServer {
//你注入的业务Service
@Autowired
private BeanService BeanService;
//存放用户信息的map
private Map<String,Session> concurrentHashMap=new ConcurrentHashMap<>();
@OnOpen
public void onOpen(@PathParam("name")String name, Session session) {
concurrentHashMap.put(name,session);
String id = session.getId();
System.err.println("通道打开"+id);
}
@OnClose
public void onClose(@PathParam("name")String name,Session session) {
concurrentHashMap.remove(name);
String id = session.getId();
System.err.println("通道关闭"+id);
}
@OnError
public void onError(Session session,Throwable error){
System.err.println("WebSocket发生错误");
error.printStackTrace();
}
@OnMessage
public void onMessage(String message, Session session) throws IOException, InterruptedException {
//你业务层操作的方法
MyBean bean = beanService.getByUniqueKey("xx","xx", MyBean .class);
for(Map.Entry<String,Session> user:concurrentHashMap.entrySet()){
//这里可以开启线程发送数据更好
user.getValue().getBasicRemote().sendText("欢迎您:"+bean.toString());
}
}
- 添加了一个类对象ConcurrentHashMap,作用就是把每个连接的客户端信息存放到这个Map里
- 在注解@ServerEndpoint定义一个参数name,并在open和close方法接收了这个参数,就是每次webSocket开启链接过关闭连接时,都根据这个参数把客户端信息存放到Map里,就是客户端的标识。
- onMessage方法里面的就是循环将Map里的用户信息取出来并输出数据到前端。
- 一定要加@Component,因为我这里测试多客户端使用的是同一个对象,否则每次调用都会创建不同的对象,那样就存不住客户端信息了。
同时用谷歌和火狐打开此页面,点击火狐的 发送数据按钮,发现火狐和谷歌都接受到了数据,测试成功!