使用spring-websocket:
springboot websocket 默认是stomp协议, js连接需要使用sockJs。
本示例仅包括订阅发布模式:
引入依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
配置websocket:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS();
}
}
定时发布消息:
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;
@Scheduled(fixedDelay = 1000L)
public void send() {
Greeting greeting = new Greeting("Hello, ");
simpMessagingTemplate.convertAndSend("/topic/greetings", greeting);
}
function connect() {
// var socket = new SockJS('/gs-guide-websocket');
var socket = new SockJS('http://localhost:8080/websocket1');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
// 订阅/topic/greetings的消息
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
该示例也是springwebsocket官方文档推荐的写法。
使用传统websocket方式:
引入依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
配置websocket
@ServerEndpoint("/websocket")
@Component
public class WebSocketServer {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
private Session session;
/* 用于存储websocket连接,key为sessionId */
private static ConcurrentHashMap<String, WebSocketServer> webSocketServerConcurrentHashMap = new ConcurrentHashMap();
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketServerConcurrentHashMap.put(session.getId(), this);
System.out.println("WebSocket opened: " + session.getId());
}
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("WebSocket message received: " + message);
String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis());
try {
//发送的消息也返回给当前连接,用于展示
session.getBasicRemote().sendText(dateStr + "发送消息:" + message);
//写入DB或者其他存储系统中。。。
} catch (IOException e) {
e.printStackTrace();
}
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
webSocketServerConcurrentHashMap.remove(session.getId());
System.out.println("WebSocket closed: " + closeReason);
}
@OnError
public void onError(Session session, Throwable throwable) {
System.out.println("WebSocket error: " + throwable);
}
/**
* 模拟服务端消息推送,5s推送一次(服务端 -> 客户端)
*/
@Scheduled(fixedRate = 5000)
public void sendMessageToClient() {
//没有连接时不做任何事情
if (CollectionUtils.isEmpty(webSocketServerConcurrentHashMap)){
return;
}
System.out.println("服务端发送消息到客户端");
String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis());
long number = new Random().nextInt(10000);
webSocketServerConcurrentHashMap.forEach((k, v) -> {
try {
v.session.getBasicRemote().sendText(dateStr + "收到消息:" + number);
//写入DB或者其他存储系统中。。。
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
前端需要用原生websocket方式连接, 使用sockJs无法通信。
//建立websocket连接
function connect() {
var endpoint = "ws://localhost:8080/websocket";
socket = new WebSocket(endpoint);
socket.onopen = function (event) {
console.log("WebSocket opened: " + endpoint);
}
socket.onmessage = function (event) {
console.log("WebSocket message received: " + event.data);
addMessageToList(event.data);
}
socket.onclose = function (event) {
console.log("WebSocket closed");
}
}
STOMP,Streaming Text Orientated Message Protocol,是流文本定向消息协议,是一种为MOM(Message Oriented Middleware,面向消息的中间件)设计的简单文本协议。
它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互,类似于OpenWire(一种二进制协议)。
由于其设计简单,很容易开发客户端,因此在多种语言和多种平台上得到广泛应用。其中最流行的STOMP消息代理是Apache ActiveMQ
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
直接使用WebSocket就很类似于使用TCP套接字来编写Web应用。因为没有高层级的线路协议,因此就需要我们定义应用之间所发送消息的语义,还需要确保连接的两端都能遵循这些语义。
就像HTTP在TCP套接字之上添加了请求-响应模型层一样,STOMP在WebSocket之上提供了一个基于帧的线路格式(frame-based wire format)层,用来定义消息的语义。与HTTP请求和响应类似,STOMP帧由命令、一个或多个头信息以及负载所组成。
因此对于stomp协议,前端需要使用stompClient 的方式进行通信。
对于websocket协议,前端使用websocket 的方式通信。