190421-SpringBoot高级篇WEB之websocket的使用说明

文章目录
  1. I. 前期准备
    1. 1. 配置如下
    2. 2. 辅助文档
  2. II. WebSocket HelloWorld版构建
    1. 1. configuration 配置
    2. 2. WebSocketHandler 处理类
    3. 3. 测试
  3. III. WebSocket 实现简单的聊天
    1. 1. RealTalkWebSocketHandler
    2. 2. 注册handler
    3. 3. 测试
    4. 4. 小结
  4. II. 其他
    1. 0. 项目
    2. 1. 一灰灰Blog
    3. 2. 声明
    4. 3. 扫描关注

常见的web应用大多是提供基础的REST服务,简单来讲就是用户发起一个请求,然后给出反应,可以理解为由客户主动发起的单边操作;那么有没有一种技术是服务端主动发起,与客户端进行交互的?

非常常见的几个需求场景,如聊天室的实现,股票的委托、成交实时刷新,信息推送机制,应用日志实时刷新等用我们传统的web交互方式,就不太容易做到了,本篇博文将介绍下HTML5中引入的WebSocket,可以如何实现客户端和服务端之间的双端通信

I. 前期准备

大环境依然是选择SpringBoot来快速构建应用

1. 配置如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.45</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>

<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
</build>

<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

从依赖中看,最关键的就是 spring-boot-starter-websocket 的引入

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2. 辅助文档

因为之前没怎么用过webscoket,所以也不知道怎么来实现一个webscoket的服务,所以就拿官方的教程进行熟悉

本文将搭建一个WebSocket的服务,那么就需要有一个对应的客户端了,目前正好在学习python,所以决定采用python来作为消费方,下文是python作为消费的教程

II. WebSocket HelloWorld版构建

接下来进入正题,开始搭建一个简单的websocket服务端

1. configuration 配置

首先是添加配置文件,和我们普通的一个类上添加@Configuration注解不一样的是,这个配置文件要求实现接口WebSocketConfigurer

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
@EnableWebSocket
public class AutoConfiguration implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "wsdemo");
}

public WebSocketHandler myHandler() {
return new MyWebSocketHandler();
}
}

注意上面的配置文件,两点

  • 添加注解 @EnableWebSocket 表示开启了webocket服务
  • registerWebSocketHandlers 这个方法用来注册websocket服务,上面表示将路径 wsdemo 的请求转发到 WebSocketHandler

2. WebSocketHandler 处理类

上面提到了WebSocketHandler,基本上也就可以确认,这个对象将是核心处理类,主要的业务逻辑就在里面,下面是我们自定义实现的一个简单的TextWebSocketHandler

1
2
3
4
5
6
7
8
9
10
public class MyWebSocketHandler extends TextWebSocketHandler {
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 获取接收到的数据
String payload = message.getPayload();

// 向客户端发送数据
session.sendMessage(new TextMessage("response: " + payload));
}
}

这个实现就简单了,重写了方法handleTextMessage,当接受到用户发送的数据时,凭借一个 response: 的头之后返回;返回数据则是借助WebSocketSession即与客户端之间的会话,来发送数据的

到这里一个基本的websocket服务端搭建完成,然后我们开始实验一下

3. 测试

先完成启动类,启动应用程序

1
2
3
4
5
6
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}

对应的python消费核心代码如下

1
2
3
4
5
6
7
8
from websocket import create_connection

ws = create_connection("ws://127.0.0.1:8080/wsdemo")
# 发送数据
ws.send("hello world")
# 接收数据
result = ws.recv()
print(result)

过程演示示意图

测试示意图

从图中的演示过程,基本上一个消费者发送数据之后,就可以获取到对应的返回数据;且消费者A不能获取消费者B返回的数据;上面的演示中,当客户端不发送数据,直接调用获取数据时,因为服务端一直没有返回数据,所以一直在等待,这样一来这个服务看起来和我们传统的rest服务没啥太大区别,都要发送请求/返回结果,并没有体现到websocket的主动推送的功能

III. WebSocket 实现简单的聊天

上面的例子,只是演示了最最基本的WebSocket的使用方式,然而并不能给我们带来WebScoket的优越性的既视感,下面准备基于WebSocket搭建一个简单的聊天室,当有新的连接进来时,推送所有人欢迎xxx;当有人发送消息时,同步给其他所有在线的小伙伴;当有小伙伴离开时,告诉所有小伙伴xx离开了

1. RealTalkWebSocketHandler

通过前面知道WebSocketHandler是处理websocket核心业务逻辑的类,因此我们新的实现中,需要记录每个连接的回话,创建连接时,发送给所有在线的同学一条欢迎消息;离线也一样;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class RealTalkWebSocketHandler extends TextWebSocketHandler {

private static Set<WebSocketSession> tmpCache = new HashSet<>();

@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
super.afterConnectionEstablished(session);
tmpCache.add(session);
sendMsg("欢迎" + session.getId() + "进入聊天室");
}

@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
super.afterConnectionClosed(session, status);
tmpCache.remove(session);
sendMsg(session.getId() + " 离开聊天室");
}


@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 获取接收到的数据
String payload = message.getPayload();
sendMsg(session.getId() + " : " + payload);
}


private void sendMsg(String msg) throws IOException {
for (WebSocketSession session : tmpCache) {
session.sendMessage(new TextMessage(msg + " | " + LocalDateTime.now()));
}
}
}

注意上面的实现

  • afterConnectionEstablished 这个方法时在了解创立之后调用的,正好用来发送欢迎词语
  • afterConnectionClosed 这个是断开连接时调用,用来发送离开词
  • handleTextMessage 这个则是接收用户发送消息的方法,和前面不一样,我们接收到消息之后,把这个消息广播给所有在线的小伙伴

2. 注册handler

在前面的配置类中,注册下我们新加的Handler

1
2
3
4
5
6
7
8
9
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "wsdemo");
registry.addHandler(realTalkWebSocketHandler(), "talk");
}

public WebSocketHandler realTalkWebSocketHandler() {
return new RealTalkWebSocketHandler();
}

3. 测试

接下来进行测试环节,看下是否满足我们的需求

测试示意图

上图中,开了三个客户端,当有新的小伙伴进入时,所有人都可以收到欢迎的话语,当其中一个人说话之后,所有人也可以获取到说的内容;基本上可以实现我们前面提到的场景

4. 小结

使用websocket搭建服务端,一般步骤比较简单,两步

  • 配置文件
    • 实现接口 WebSocketConfigurer
    • 添加注解 @EnableWebSocket
    • 注册WebSocketHandler
  • 实现自定义WebSocketHandler
    • 继承 AbstractWebSocketHandler, 实现具体的连接/断连/接收消息的处理逻辑

双端通讯

服务端和客户端的通讯,主要借助的是WebSocketSession来实现,因此在我们自己实现的简易版聊天室中,需要自己来保存所有客户端的会话

那么问题来了,有没有更简单的方式来实现聊天室的这种场景呢,session的管理应该属于普适性的需求了,如果都需要开发者自己来处理的话,上手成本也大了点吧

官方教程中websocket这一节提到了STOMP,后续博文将介绍如何使用STOMP,结合websocket实现更复杂的双端通信场景

II. 其他

0. 项目

1. 一灰灰Blog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,以上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

QrCode

知识星球

goals


打赏 如果觉得我的文章对您有帮助,请随意打赏。
分享到