引言
本文将介绍前后端引入WebSocket的整个流程。
WebSocket 的实现分为客户端和服务端两部分,客户端(通常为浏览器)发出 WebSocket 连接请求,服务端响应,实现类似 TCP 握手的动作,从而在浏览器客户端和 WebSocket 服务端之间形成一条 HTTP 长连接快速通道。两者之间后续进行直接的数据互相传送,不再需要发起连接和相应。
一、引入WebSocket依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
二、WebSocketConfig
@Configuration
public class WebSocketConfig extends ServerEndpointConfig.Configurator {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
三、RedisUtils
public List<Object> getForList(String key) {
//获取两个人信箱里所有的信息
return redisTemplate.opsForList().range(key, 0, -1);
}
public void setForList(String key, Object value) {
//向正在发送信息的任意两人的信箱中中添加信息
redisTemplate.opsForList().rightPush(key, value);
}
四、WebSocketServer
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageEntity implements Serializable {
// 发送者的 id
private Long from;
// 接受者的 id
private Long to;
// 具体信息
private String message;
// 发送时间
private Date time;
}
@onMessage 注解的 Java 方法用于接收传入的 WebSocket 信息,这个信息可以是文本格式,也可以是二进制格式。
@OnOpen 在这个端点一个新的连接建立时被调用。参数提供了连接的另一端的更多细节。Session 表明两个 WebSocket 端点对话连接的另一端,可以理解为类似 HTTPSession 的概念。
@OnClose 在连接被终止时调用。参数 closeReason 可封装更多细节,如为什么一个 WebSocket 连接关闭。
@ServerEndpoint(
value = "/websocket/{uId}",
configurator = WebSocketConfig.class)
@Component
public class WebSocketServer {
private static final Map<Long,Session> onlineUsers = new ConcurrentHashMap<Long, Session>();
private Session session;
private static RedisUtils redisUtils;
@Autowired
public void setRedisUtils(RedisUtils redisUtils) {
WebSocketServer.redisUtils = redisUtils;
}
@OnOpen
public void onOpen(@PathParam("uId")String uId, Session session){
this.session = session;
onlineUsers.put(Long.valueOf(uId),session);
}
@OnMessage
public void onMessage(String s) throws IOException {
if(s.equals("ping")) {
return;
}
MessageEntity message = JSON.parseObject(s, MessageEntity.class);
// 将信息存储到 redis 中
String key = LongStream.of(message.getFrom(), message.getTo())
.sorted()
.mapToObj(String::valueOf)
.collect(Collectors.joining("-"));
redisUtils.setForList(key, message);
if(!message.getFrom().equals(message.getTo())) {
Session onsession = onlineUsers.get(message.getTo());
// 如果用户在线就将信息发送给指定用户
if (onsession != null) {
onsession.getBasicRemote().sendText(JSON.toJSONString(message));
}
}
}
@OnClose
public void onClose(Session session){
for (Map.Entry<Long, Session> entry : onlineUsers.entrySet()) {
if (this.session.getId().equals(entry.getValue().getId())) {
onlineUsers.remove(entry.getKey());
return;
}
}
}
@OnError
public void onError(Throwable error) {
error.printStackTrace();
}
}
五、定义接口获取两个人的信息
@PostMapping("/pullMsg")
public ResponseResult pullMsg(@RequestParam("from") Long from, @RequestParam("to") Long to){
// 根据两人的 id 生成键,并到 redis 中获取
String key = LongStream.of(from, to)
.sorted()
.mapToObj(String::valueOf)
.collect(Collectors.joining("-"));
return ResponseResult.ok(redisUtils.getForList(key));
}
六、前端WebSocket
WebSocketServer 连接成功后,会触发 onopen 消息;如果连接失败,发送、接收数据失败或者处理数据出现错误, 会触发 onerror 消息;当 接收到 WebSocketServer 发送过来的数据时,就会触发 onmessage 消息,参数 event 中包含 Server 传输过来的数据;当 接收到 WebSocketServer 端发送的关闭连接请求时,就会触发 onclose 消息。
data() {
return {
msg: '',
socket: null,
user: {},
msgList: []
}
},
methods: {
createWebSocket(){
// 渲染界面时, 根据用户的 id 获取 websocket 连接
this.socket = new WebSocket(`ws://127.0.0.1:7878/websocket/${this.user.id}`)
this.socket.onopen = this.websocketopen;
this.socket.onerror = this.websocketonerror;
this.socket.onmessage = this.websocketmessage
this.socket.onclose = this.websocketclose;
window.onbeforeunload = () => {
this.socket.close();
};
this.$router.afterEach(() =>{
this.socket.close()
})
},
sendMsg() {
if(this.msg == '' || this.msg.trim() == ''){
return this.$message.warning("发送内容不能为空!")
}
let entity = {
from: this.user.id,
to: this.contact.id,
message: this.msg,
time: new Date()
}
this.socket.send(JSON.stringify(entity))
this.msgList.push(entity)
this.msg = ''
},
websocketonerror(e){
console.log("WebSocket连接发生错误" + e);
reconnect()
},
websocketopen(){
console.log("websocket打开")
},
websocketclose(e){
this.socket.close()
console.log("websocket关闭")
},
websocketmessage(event){
this.msgList.push(JSON.parse(event.data))
},
}