https://blog.csdn.net/hry2015/article/details/79838194
https://blog.csdn.net/yingxiake/article/details/51213060
https://blog.csdn.net/u011429231/article/details/82498795
https://www.cnblogs.com/selwynHome/p/9609298.html
前端代码:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title></title>
</head>
<body onload="disconnect()">
<div>
<div>
<button id="connect" onclick="connect();">连接</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>
</div>
<div id="conversationDIV">
<label>测试</label>
<input type="text" id="name"/>
<button id="sendName" onclick="sendName();">发送</button>
<p id="response"></p>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.js"></script>
<script type="text/javascript">
var stompClient = null;
function setConnected(connected){
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDIV').style.visibility = connected ? 'visible':'hidden';
$('#response').html();
}
function connect(){
//链接Socket的endpoint名称为endpointWisely
var socket = new SockJS('http://localhost:8886/scdc-websocket');
//使用STOMP子协议的WebSocket客户端
stompClient = Stomp.over(socket);
//链接WebSocket服务端
stompClient.connect({},function (frame) {
setConnected(true);
console.log('Connected:' + frame);
//通过stompClient.subscribe订阅/topic/getResponse目标发送的消息,即控制器中的@SendTo
stompClient.subscribe('/topic/scdc.alarm',function (response) {
showResponse(response);
});
});
}
function disconnect(){
if(stompClient != null){
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnecteed");
}
function sendName(){
var name = $('#name').val();
stompClient.send("/welcome",{},name);
}
function showResponse(message){
$('#response').append($('#response').val() + message + '<br/>');
}
</script>
</body>
</html>
后端不采用stomp的方式
package com.ac.webSocket;
import net.sf.json.JSONObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import com.ac.constants.ErrorCodeConstants;
import com.ac.constants.ErrorMsgConstants;
import com.common.utils.StringUtil;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint("/websocket/{clientId}")
public class WebSocket {
private static final Logger logger = Logger.getLogger(WebSocket.class);
// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static AtomicInteger onlineCount = new AtomicInteger(0);
// concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
//若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
// private static CopyOnWriteArraySet<WebSocket> webSocketSet = new
// CopyOnWriteArraySet<WebSocket>();
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
//记录每个客户端的实例变量, 现在拿下面的全局map记录
//private Session session;
private static Map<String, Session> webSocketMap = new ConcurrentHashMap<String, Session>();
/**
* 连接建立成功调用的方法
*
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(@PathParam("clientId") String clientId, Session session) {
// 用登录用户编号和sessionId的拼接来做webSocket通信的唯一标识
String key = getWebSocketMapKey(clientId, session);
webSocketMap.put(key, session);
addOnlineCount(); // 在线数加1
logger.info("WebSocket有新连接加入!当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(@PathParam("clientId") String clientId, Session session, CloseReason closeReason) {
String key = getWebSocketMapKey(clientId, session);
webSocketMap.remove(key, session);
subOnlineCount(); // 在线数减1
logger.info("WebSocket有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(@PathParam("clientId") String clientId, String message, Session session) {
logger.info("WebSocket收到来自客户端的消息:" + message);
}
/**
* 获取webSocketMap集合的Key
*
* @param clientId 用户编号
* @param session webSocket的Session
* @return
*/
private String getWebSocketMapKey(String clientId, Session session) {
if (StringUtil.isNil(clientId)) {
return session.getId();
} else {
return clientId + "_" + session.getId();
}
}
/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
logger.error("WebSocket发生错误", error);
}
// 群发消息
public static void doSend(String message) {
if (webSocketMap.size() > 0) {
for (Map.Entry<String, Session> entry : webSocketMap.entrySet()) {
try {
sendMessage(entry.getValue(), message);
} catch (IOException e) {
logger.error("WebSocket doSend is error:", e);
continue;
}
}
}
}
public static void sendMessage(Session session, String message) throws IOException {
session.getBasicRemote().sendText(message);
}
public static int sendMessageByClientIdList(List<String> clientIdList, String message) {
int status = 0;
if (CollectionUtils.isNotEmpty(clientIdList)) {
for (String clientId : clientIdList) {
status = sendMessageByClientId(clientId, message);
}
}
return status;
}
/**
* 通过用户的编号来发送webSocket消息
*
* @param clientId
* @param message
*/
public static int sendMessageByClientId(String clientId, String message) {
int status = 0;
if (webSocketMap.size() > 0) {
for (Map.Entry<String, Session> entry : webSocketMap.entrySet()) {
try {
String key = entry.getKey();
// 判断webSocketMap中的clientId和发送的clientId是否相同
// 若相同则进行发送消息
String key1 = key.substring(0, key.lastIndexOf("_"));
if (key1.equals(clientId)) {
sendMessage(entry.getValue(), message);
status = 200;
}
} catch (IOException e) {
logger.error("WebSocket doSend is error:", e);
continue;
}
}
}
return status;
}
public static void sendSpeechMessageByClientId(String clientId, String message) {
if (webSocketMap.size() > 0) {
for (Map.Entry<String, Session> entry : webSocketMap.entrySet()) {
try {
String key = entry.getKey();
// 判断webSocketMap中的clientId和发送的clientId是否相同
// 若相同则进行发送消息
String key1 = key.substring(0, key.lastIndexOf("_"));
if (key1.equals(clientId)) {
sendMessage(entry.getValue(), message);
}
} catch (IOException e) {
logger.error("WebSocket doSend is error:", e);
continue;
}
}
}
}
public static synchronized AtomicInteger getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocket.onlineCount.getAndIncrement();
}
public static synchronized void subOnlineCount() {
WebSocket.onlineCount.getAndDecrement();
}
}
后端采用stomp的方式
config:
package com.websocket;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
*
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.stomp.port}")
private Integer stompPort;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${spring.rabbitmq.virtual-host}")
private String virtualHost;
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/scdc-websocket")
.setAllowedOrigins("*")
.withSockJS();
}
/**
* enableSimpleBroker 服务端推送给客户端的路径前缀
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//registry.enableSimpleBroker("/topic");
registry.enableStompBrokerRelay("/topic", "/queue")
.setRelayHost(host)
.setRelayPort(stompPort)
.setClientLogin(username)
.setClientPasscode(password)
.setSystemLogin(username)
.setSystemPasscode(password)
.setVirtualHost(virtualHost);
registry.setApplicationDestinationPrefixes("/app");
}
}
service:
package com.service.impl;
import com.alibaba.fastjson.JSON;
import com.common.constants.Constants;
import com.common.dto.elevator.FaceResultReqDto;
import com.common.dto.ws.AddDeviceAlarmDto;
import com.common.dto.ws.EventParam;
import com.common.dto.ws.RuleParam;
import com.common.enums.BuildEnum;
import com.common.enums.ResultEnum;
import com.common.exception.Exception;
import com.common.service.CommonWsService;
import com.common.utils.*;
import com.dto.AlarmSendStr;
import com.service.WebSocketService;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.text.SimpleDateFormat;
import java.util.*;
/**
*
*
* webSocket推送消息
*/
@Service
public class WebSocketServiceImpl implements WebSocketService {
private static final Logger log = Logger.getLogger(WebSocketServiceImpl.class);
@Autowired
private SimpMessagingTemplate messagingTemplate;
@Value("${websocket.alarm.topic}")
private String websocketAlarmTopic;
@Override
@Transactional(rollbackFor = Exception.class)
public void sendAlarmMsg(AlarmSendStr listDto) {
messagingTemplate.convertAndSend(websocketAlarmTopic+"."+listDto.getSpCode(),listDto);
}
@Override
public void sendAlarmCommon(AlarmSendStr listDto) {
messagingTemplate.convertAndSend("/topic/scdc.alarm",listDto);
}
}