Spring Boot为嵌入式Tomcat(8和7),Jetty 9和Undertow提供WebSockets自动配置。
如果要将war文件部署到独立容器,Spring Boot会假定容器将负责其WebSocket支持的配置。
Spring提供了丰富的WebSocket支持,可以通过spring-boot-starter-websocket模块轻松访问。
示例:(基于websockt协议)
用的<<深入浅出SpringBoot 2.x>>书中的例子:
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端,这样就能够从客户端发送消息到服务器,而服务器又可以转发消息到客户端,这样就能够实现客户端之间的交互。对于WebSockt的开发,Spring提供了良好的支持。目前很多浏览器己经实现了Web Socket 协议,但是依旧存在着很多浏览器
没有实现该协议,为了兼容那些没有实现该协议的浏览器, 还需要通过STOMP 协议来完成这些兼容。
1. 添加websockt依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2. 定义服务器端点
定义WebSocket服务器的端点,这样客户端就能请求服务器的端点:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
// 创建服务器端点
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
3. 定义服务端站点
步骤2中定义了服务器端点,这时候就可以使用@ServerEndpoint定义一个端点服务类。可以定义WebSocket的打开、关闭、错误和发送消息的方法。
每一个客户端打开时,都会为其创建一个WebSocketService对象,所以这打开方法中都会去计数并且将这个对象保存到CopyOnWriteArraySet中,这样就可以知道拥有多少连接。对于关闭方法则是清除这个对象,并且计数减一。对于消息发送方法,则是通过轮询对所有的客户端连接都给予发送消息,所以所有的连接都可以收到这个消息。但是有时候可能只是需要发送给特定的用户,则需要得到用户的信息,然后再发送给特定的用户。
用到的注解:
- @Serv巳rEndpoint('/ws'):表示让Spring创建WebSocket的服务端点,其中请求地址是'/ws'。
- @OnOpen:标注客户端打开WebSocket服务端点调用方法。
- @OnClose: 标注客户端关闭WebSocket服务端点调用方法。
- @OnMessage:标注客户端发送消息,WebSocket 服务端点调用方法。
- @OnError:标注客户端请求WebSocket 服务端点发生异常调用方法。
import org.springframework.stereotype.Service;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
// 表示让Spring创建WebSocket的服务端点,请求地址是 /ws
@ServerEndpoint("/ws")
@Service
public class WebSocketService {
private static int onlineCount = 0;
private static CopyOnWriteArraySet<WebSocketService> webSocketServiceSet = new CopyOnWriteArraySet<>();
private Session session;
public Session getSession() {
return session;
}
// 标注客户端打开WebSocket服务端点调用方法
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketServiceSet.add(this);
addOnlineCount();
System.out.println("有新连接加入,当前在线人数:" + getOnlineCount());
try {
sendMessage("有新连接加入了!");
} catch (Exception e) {
System.out.println("IO异常");
}
}
// 标注客户端关闭WebSocket服务端点调用方法
@OnClose
public void onClose() {
webSocketServiceSet.remove(this);
subOnlineCount();
System.out.println("有连接关闭,当前在线人数:" + getOnlineCount());
}
// 标注客户端发送消息,WebSocket服务端点调用方法
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
for (WebSocketService webSocketService : webSocketServiceSet) {
try {
// 获取当前用户名称
// String userName = webSocketService.getSession().getUserPrincipal().getName();
webSocketService.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 标注客户端请求WebSocket服务端点发生异常调用方法
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
private void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
private static synchronized int getOnlineCount() {
return onlineCount;
}
private static synchronized void addOnlineCount() {
WebSocketService.onlineCount++;
}
private static synchronized void subOnlineCount() {
WebSocketService.onlineCount--;
}
}
4. 客户端页面
定义了一个Web Socket 的JavaScript 对象,其中给出了ws 的地址,路径“/ws ”代表连接服务器的哪个端点,这样就能定位到WebSocketServiceimpl 服务类中,然后定义了其打开、关闭、错误和接收消息方法,这样这个页面就可以与服务器端点通信。
打开两个页面,进行护发消息测试。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket测试</title>
</head>
<body>
<p>测试WebSocket站点</p>
<input id="message" type="text" />
<button onclick="sendMessage()">发送消息</button>
<button onclick="closeWebSocket()">关闭WebSocket连接</button>
<div id="context"></div>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="application/javascript">
var websocket = null;
// 判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
// 创建WebSocket 对象,连接服务器端点
websocket = new WebSocket("ws://localhost:8080/ws");
} else {
alert('Not support websocket');
}
// 连接发生错误的回调方法
websocket.onerror = function() {
appendMessage ("error");
}
// 连接成功建立的回调方法
websocket.onopen = function(event) {
appendMessage ("open");
}
// 接收到消息的回调方法
websocket.onmessage = function (event) {
appendMessage(event.data);
}
websocket.onclose = function() {
appendMessage("close");
}
websocket.onbeforeupload = function() {
websocket.close();
}
function appendMessage(message) {
var context = $('#context').html() + '<br>' + message;
$('#context').html(context);
}
function closeWebSocket() {
websocket.close();
}
function sendMessage() {
var message = $('#message').val();
websocket.send(message);
}
</script>
</body>
</html>