前言
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
小试牛刀
这次只是简单了解下websocket,先把项目搭建起来,让项目能够跑起来,万事开头难,项目能够跑起来就说明最难的问题已经解决了。
后端
导入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
配置类
@Configuration
public class WebSocketConfig {
/**
* 注入ServerEndpointExporter,自动注册使用@ServerEndpoint注解
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
端点
@Slf4j
@Component
@ServerEndpoint(value = "/chat/{username}")
public class ChatEndpoint {
//存储每一个客户端对象对应的chatEndpoint对象
private static Map<String, ChatEndpoint> onlineUsers = new ConcurrentHashMap<>();
//和某个客户端连接对象,需要通过他来给客户端发送数据
private Session session;
private String username;
/**
* 建立成功时调用
* 将当前会话建立成功的用户会话保存起来
* @param session
* @param username
*/
@OnOpen
public void onOpen(Session session, @PathParam(value = "username") String username) {
this.session = session;
//保存用户
onlineUsers.put(username, this);
this.username = username;
log.info("用户连接成功 = [{}]", username);
}
/**
* 接收到消息时调用
* @param message
* @param session
*/
@OnMessage
public void onMessage(String message, Session session) {
//解析用户发送来的数据
log.info("用户消息 = [{}]", message);
//将消息广播给其他用户
Map<String, ChatEndpoint> onlineUsers = ChatEndpoint.onlineUsers;
Set<String> keySet = onlineUsers.keySet();
Iterator<String> it = keySet.iterator();
while (it.hasNext()) {
String username = it.next();
if (this.username.equals(username)) {
continue;
}
ChatEndpoint chatEndpoint = onlineUsers.get(username);
chatEndpoint.session.getAsyncRemote().sendText(this.username + ":" + message);
}
}
@OnClose
public void onClose(Session session) {
onlineUsers.remove(this.username);
log.info("用户关闭成功 = [{}]", username);
}
}
websocket实现有编程式和注解式,而我采用的是注解式,我并没有对连接的用户做身份验证,只是简单的先让项目能够跑起来,后续再慢慢优化。
至此,启动项目即可,接下来就是通过前端来连接。
前端
前端我采用的是Vue来实现,因为日常主要从事后端开发工作,所以以前学的Vue都丢掉了,现在要慢慢一点点重新捡起来了。
首先,如果没有安装node环境的童鞋需要先安装,安装过程也是很简单的,可以自行百度搜索一下,就几行命令。
先创建一个Vue项目,在前端的工作目录下进入cmd窗口执行 Vue create xxxx(项目名),然后选择Vue版本,我创建的是Vue3,选择好就等待创建完成即可。
tips:执行vue create xxx命令可能需要等一会儿.
选择vue的版本,此处我选择的是3,直接回车即可,然后等待项目创建完成。
项目创建完成后可以用开发工具打开,我用的开发工具是webstorm,因为作为一名后端开发习惯了idea的开发页面。
下载websocket依赖
然后我们简单的在项目自带的HelloWorld组件上面进行代码编写
<template>
<div>
<span>用户名:</span>
<input v-model="username">
<button v-on:click="connectServer">连接服务器</button><br/>
<input v-model="msg"/>
<button v-on:click="sendMsg">发送消息</button><br/>
</div>
</template>
<script>
export default{
name: "HelloWorld",
methods: {
sendMsg() {
this.socket.send(this.msg)
console.log("向服务器发送消息" + this.msg);
},
connectServer () {
console.log("开始连接...")
const endpoint = 'ws://localhost:8082/chat/' + this.username; // WebSocket的地址
this.socket = new WebSocket(endpoint);
this.socket.addEventListener('message', (event) => {
// 处理服务器发送的消息
console.log("服务器:" + event.data);
});
this.socket.addEventListener('close', () => {
// 连接关闭
console.log('连接关闭')
});
}
},
data() {
return {
username: "",
msg: ""
}
}
}
</script>
在这里我是将内容打印在控制台上面,所以需要F12进入开发者工具的控制台中看打印内容。
最后一步,启动前端项目,执行 npm run serve 即可。
先输入用户名连接服务器,再用其它浏览器打开窗口访问 localhost:8080 同操作,发送消息即可广播给其它用户。
tips: websocket难点在于session对象无法序列化,分布式情况下需要解决找到上一次连接所在节点,所以后续我会一步步解决这个问题。