1、加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
2、编写配置类:开启WebSocket服务端的自动注册。
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
注意:ServerEndpointExporter 是由Spring官方提供的标准实现,用于扫描ServerEndpointConfig配置类和@ServerEndpoint注解实例。使用规则也很简单:1.如果使用默认的嵌入式容器 比如Tomcat则必须手工在上下文提供ServerEndpointExporter。2. 如果使用外部容器部署war包,则不要提供提供ServerEndpointExporter,因为此时SpringBoot默认将扫描服务端的行为交给外部容器处理
3、创建WebSocket服务端
- 通过注解
@ServerEndpoint
来声明实例化WebSocket服务端。(注意配置@Component
才能被spring扫描) - 通过注解
@OnOpen
、@OnMessage
、@OnClose
、@OnError
来声明回调函数。
事件类型 | WebSocket服务端注解 | 事件描述 |
---|---|---|
open | @OnOpen (Session session) | 当打开连接后触发 |
message | @OnMessage(Session session,String message) | 当客户端接收服务端数据时触发 |
error | @OnClose(Session session) | 当通信异常时触发 |
close | @OnError(Session session,Throwable error) | 当连接关闭时触发 |
- 发送消息:
new Session().getBasicRemote().sendText(message);
示例代码如下:
package com.test.service.server;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.gson.Gson;
import com.test.model.repose.Leaderboard;
import com.test.model.request.BasePaginationQuery;
import com.test.service.SubmissionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
@Slf4j
@Component
@ServerEndpoint(value = "/websocket/{id}")
public class WebSocketServer {
/**
* concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象。
* 虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。
*/
public static CopyOnWriteArraySet<WebSocketServer> webSocketServerSet
= new CopyOnWriteArraySet<>();
/**
* 在线连接数,应该把它设计成线程安全的
*/
private static int onlineCount = 0;
private static SubmissionService submissionService;
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 会话窗口的ID标识
*/
private String id = "";
public static synchronized int getOnlineCount() {
return onlineCount;
}
@Autowired
public void setSubmissionService(SubmissionService submissionService) {
WebSocketServer.submissionService = submissionService;
}
@PostConstruct
public void init() {
log.info("websocket 加载");
}
/**
* 链接成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("id") String id) {
log.info("onOpen >> 链接成功");
this.session = session;
//将当前websocket对象存入到Set集合中
webSocketServerSet.add(this);
//在线人数+1
addOnlineCount();
log.info("有新窗口开始监听:" + id + ", 当前在线人数为:" + getOnlineCount());
this.id = id;
try {
BasePaginationQuery basePaginationQuery = new BasePaginationQuery();
basePaginationQuery.setId(id);
IPage<Leaderboard> iPage = submissionService.selectLeaderboardByCompetitionId(basePaginationQuery);
String data = new Gson().toJson(iPage.getRecords());
sendMessage(data);
} catch (Exception e) {
log.error(e.toString());
}
}
/**
* 链接关闭调用的方法
*/
@OnClose
public void onClose() {
log.info("onClose >> 链接关闭");
//移除当前Websocket对象
webSocketServerSet.remove(this);
//在内线人数-1
subOnLineCount();
log.info("链接关闭,当前在线人数:" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message
// * @param session
*/
@OnMessage
public void onMessage(String message) {
log.info("接收到窗口:" + id + " 的信息:" + message);
//发送信息
for (WebSocketServer webSocketServer : webSocketServerSet) {
try {
BasePaginationQuery basePaginationQuery = new BasePaginationQuery();
basePaginationQuery.setId(id);
IPage<Leaderboard> iPage = submissionService.selectLeaderboardByCompetitionId(basePaginationQuery);
String data = new Gson().toJson(iPage.getRecords());
webSocketServer.sendMessage(data);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@OnError
public void onError(Session session, Throwable e) {
e.printStackTrace();
}
/**
* 推送消息
*
* @param message
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 群发消息
*
* @param message
* @throws IOException
*/
public void broadCastInfo(String message) throws IOException {
for (WebSocketServer webSocketServer : webSocketServerSet) {
if (webSocketServer.session.isOpen()) {
webSocketServer.sendMessage(message);
}
}
}
private void subOnLineCount() {
WebSocketServer.onlineCount--;
}
private void addOnlineCount() {
WebSocketServer.onlineCount++;
}
}
4、在线测试
工具:http://www.easyswoole.com/wstool.html