前后端使用利用WebSocket进行通信
Posted weixin_45747080
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前后端使用利用WebSocket进行通信相关的知识,希望对你有一定的参考价值。
前后端使用利用WebSocket进行通信
1、前后端如何连接
服务端利用SpringBoot启动一个WebSocket服务,同时暴露出该服务的应用路径,客户端则利用该应用路径进行连接。需要注意的是,在服务端只需要启动一个WebSocket服务,而每一个客户端就是一个WebSocket应用。
就有点像:服务端是古老的电话接线员,而客户端就是打电话的人。假如用户A想要给用户B打电话就需要先打电话到接线员那,然后接线员再接通用户B。不过WebSocket可以实现广播和私聊。
2、前端初始化WebSocket对象
不需要引入第三方依赖包,直接使用js自带的WebSocket对象。
-
创建WebSocket对象
const ws = new WebSocket('ws://localhost:8000/websocket/')
ws://
和jdbc://
、http://
一样都是协议名,同样的,Websocket还支持更加安全的wss://
,/websocket
即该服务的应用路径名 -
onopen事件监听
与服务端连接成功会触发。
webSocketOnOpen(e){ console.log('与服务端连接打开->',e) },
-
onerror事件监听
与服务端连接异常时触发。
webSocketOnError(e){ console.log('与服务端连接异常->',e) },
-
onclose事件监听
与服务端连接关闭时触发。
webSocketOnClose(e){ console.log('与服务端连接关闭->',e) },
-
onmessage事件监听
接收到来自服务端的消息时触发。
webSocketOnMessage(e){ console.log('来自服务端的消息->',e) },
一个完整的WebSocket对象应该具备以上属性,同时需要将以上属性跟WebSocket对象绑定。
使用原生JS初始化WebSocket对象演示
const ws = new WebSocket(webSocketUrl)
//onopen事件监听
ws.addEventListener('open',e=>{
console.log('与服务端连接打开->',e)
},false)
//onclose事件监听
ws.addEventListener('close',e=>{
console.log('与服务端连接关闭->',e)
},false)
//onmessage事件监听
ws.addEventListener('message',e=>{
console.log('来自服务端的消息->',e)
},false)
//onerror事件监听
ws.addEventListener('error',e=>{
console.log('与服务端连接异常->',e)
},false)
ws对象的addEventListener( )方法,为WebSocket绑定事件监听,从而在各个事件监听中处理事务。
使用Vue初始化WebSocket对象演示
export default {
name: "Home",
data() {
return {
webSocketObject: null,
}
},
created() {
//初始化WebSocket
this.webSocketInit()
},
methods: {
webSocketInit(){
const webSocketUrl = 'ws://localhost:8000/websocket/'+this.username
this.webSocketObject = new WebSocket(webSocketUrl);
this.webSocketObject.onopen = this.webSocketOnOpen
this.webSocketObject.onmessage = this.webSocketOnMessage
this.webSocketObject.onerror = this.webSocketOnError
this.webSocketObject.onclose = this.webSocketOnClose
},
webSocketOnOpen(e){
console.log('与服务端连接打开->',e)
},
webSocketOnMessage(e){
console.log('来自服务端的消息->',e)
},
webSocketOnError(e){
console.log('与服务端连接异常->',e)
},
webSocketOnClose(e){
console.log('与服务端连接关闭->',e)
},
},
}
</script>
同样的,利用methods分别定义好OnOpen、OnMessage、OnError、OnClose四个事件监听,然后进行初始化并且绑定就可以了。
这样就完成了WebSocket对象以及事件监听的初始化。
3、后端初始化WebSocket对象
SpringBoot自带的WebSocket有以下5个注解需要注意:
-
@ServerEndpoint
暴露出的ws应用的路径,支持RESTful风格传参,类似
/websocket/{username}
-
@OnOpen
与当前客户端连接成功,有入参
Session
对象(当前连接对象),同时可以利用@PathParam()
获取上述应用路径中传递的参数,比如@PathParam("username") String username
。 -
@OnClose
与当前客户端连接失败,有入参
Session
对象(当前连接对象),同时也可以利用@PathParam()
获取上述应用路径中传递的参数。 -
@OnError
与当前客户端连接异常,有入参
Session
对象(当前连接对象)、Throwable
对象(异常对象),同时也可以利用@PathParam()
获取上述应用路径中传递的参数。 -
@OnMessage
当前客户端发送消息,有入参
Session
对象(当前连接对象)、String message
对象(当前客户端传递过来的字符串消息)
利用SpringBoot创建项目,需要引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
在application.yaml中定义好该服务的端口号:
server:
port: 8000
利用自定义配置类开启WebSocket:
package cn.wqk.serverwebsocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
@EnableWebSocket
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
定义Websocket主业务类:
package cn.wqk.serverwebsocket.socket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
@Component
@Slf4j
@ServerEndpoint("/websocket/{username}") //暴露的ws应用的路径
public class WebSocket {
/**
* 客户端与服务端连接成功
* @param session
* @param username
*/
@OnOpen
public void onOpen(Session session,@PathParam("username") String username){
/*
do something for onOpen
与当前客户端连接成功时
*/
}
/**
* 客户端与服务端连接关闭
* @param session
* @param username
*/
@OnClose
public void onClose(Session session,@PathParam("username") String username){
/*
do something for onClose
与当前客户端连接关闭时
*/
}
/**
* 客户端与服务端连接异常
* @param error
* @param session
* @param username
*/
@OnError
public void onError(Throwable error,Session session,@PathParam("username") String username) {
}
/**
* 客户端向服务端发送消息
* @param message
* @param username
* @throws IOException
*/
@OnMessage
public void onMsg(Session session,String message,@PathParam("username") String username) throws IOException {
/*
do something for onMessage
收到来自当前客户端的消息时
*/
}
}
4、前后端联动实现简单聊天室
前端在OnMessage事件监听中接收到来自后端的消息,然后进行处理(展示在页面上);同样的,后端也是在OnMessage中接收到来自前端的消息,然后进行处理(发送到所有客户端)。
前端
-
定义一个输入框,再定义一个按钮:接收消息并且发送
<input type="text" v-model="sendMessage" placeholder="请输入你要发送的消息"> <button @click="handleSendButton()">发送</button>
handleSendButton() { const username = this.username const message = this.sendMessage this.webSocketObject.send(JSON.stringify({ id: 1, message, username, time: new Date().getTime() })) this.sendMessage = '' },
**注意:**直接利用websocket对象的
send()
方法发送消息,前后端数据传输利用JSON字符串,所以发送的时候需要将对象转为JSON字符串。 -
定义一个列表:用于展示聊天信息
<table> <thead> <tr> <th>消息编号</th> <th>发送者</th> <th>发送时间</th> <th>发送内容</th> </tr> </thead> <tbody> <tr v-for="item in messageList" :key="item.time"> <td>{{ item.id }}</td> <td>{{ item.username }}</td> <td>{{ new Date(item.time).toLocaleTimeString() }}</td> <td>{{ item.message }}</td> </tr> </tbody> </table>
-
当客户端的onMessage接收到消息后就把消息展示到列表中:
webSocketOnMessage(e){ console.log('来自服务端的消息->',e) const receiveMessage = JSON.parse(e.data); this.messageList.push(receiveMessage) },
**注意:**通过console.log(e)不难发现,来自服务端的消息是存储在e.data中的,并且是JSON字符串,所以我们需要将它转为JSON对象。
此时已经完成了前端发送消息并且接收消息且展示消息了。
后端
接收消息并且群发消息:
@OnMessage
public void onMsg(Session session,String message,@PathParam("username") String username) throws IOException {
/*
do something for onMessage
收到来自当前客户端的消息时
*/
sendAllMessage(message);
}
//向所有客户端发送消息(广播)
private void sendAllMessage(String message){
Set<String> sessionIdSet = onlineClientMap.keySet(); //获得Map的Key的集合
for (String sessionId : sessionIdSet) { //迭代Key集合
Session session = onlineClientMap.get(sessionId); //根据Key得到value
session.getAsyncRemote().sendText(message); //发送消息给客户端
}
}
完整代码
<template>
<div>
<table>
<thead>
<tr>
<th>消息编号</th>
<th>发送者</th>
<th>发送时间</th>
<th>发送内容</th>
</tr>
</thead>
<tbody>
<tr v-for="item in messageList" :key="item.time">
<td>{{ item.id }}</td>
<td>{{ item.username }}</td>
<td>{{ new Date(item.time).toLocaleTimeString() }}</td>
<td>{{ item.message }}</td>
</tr>
</tbody>
</table>
<input
type="text"
v-model="sendMessage"
placeholder="请输入你要发送的消息">
<button @click="handleSendButton()">发送</button>
<button @click="handleLogoutButton()">退出</button>
</div>
</template>
<script>
import {
getUsername,
removeUsername
} from "@/utils/username";
export default {
name: "Home",
data() {
return {
webSocketObject: null,
username: '',
messageList: [
],
sendMessage: ''
}
},
created() {
//从localStorage中获得username
this.username = getUsername()
//如果username不存在返回到登录页面
if (!this.username){
this.$router.push({
name: 'Login'
})
}
//初始化WebSocket
this.webSocketInit()
},
beforeDestroy() {
this.webSocketObject.close();//在该组件销毁时关闭该连接以节约资源
},
methods: {
webSocketInit(){
const webSocketUrl = 'ws://localhost:8000/websocket/'+this.username
this.webSocketObject = new WebSocket(webSocketUrl);
this.webSocketObject.onopen = this.webSocketOnOpen
this.webSocketObject.onmessage = this.webSocketOnMessage
this.webSocketObject.onerror = this.webSocketOnError
this.webSocketObject.onclose = this.webSocketOnClose
},
webSocketOnOpen(e){
console.log('与服务端连接打开->',e)
},
webSocketOnMessage(e){
console.log('来自服务端的消息->',e)
const receiveMessage = JSON.parse(e.data);
this.messageList.push(receiveMessage)
},
webSocketOnError(e){
console.log('与服务端连接异常->',e)
},
webSocketOnClose(e){
console.log('与服务端连接关闭->',e)
},
handleSendButton() {
const username = this.username
const message = this.sendMessage
this.webSocketObject.send(JSON.stringify({
id: 1,
message,
username,
time: new Date().getTime()
}))
this.sendMessage = ''
},
handleLogoutButton(){
removeUsername() //清除username然后断开连接
this.webSocketObject.close();
this.$router.push({
name: 'Login'
})
}
},
}
</script>
Tips:
我这里采用的是在上一个页面获取到用户的用户名然后存储到LocalStorage中。
package cn.wqk.serverwebsocket.socket;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
@Component
@Slf4j
@ServerEndpoint("/websocket/{username}") //暴露的ws应用的路径
public class WebSocket {
/** 当前在线客户端数量(线程安全的) */
private static AtomicInteger onlineClientNumber = new AtomicInteger(0)以上是关于前后端使用利用WebSocket进行通信的主要内容,如果未能解决你的问题,请参考以下文章