1. 说明

websocket是一个全双工的网络协议,可以双向发送消息或接收消息,对于服务端与客户端之间的通信或者客户端相互通信十分友好
概念不多说,下面就来搭建一个实际的工程

2. 工程搭建

这里的工程搭建springboot结合spring来使用,整合spring的工程;其余的方法可以参见
websocket种工程实现办法

2.1. 引入依赖

1
2
3
4
5
6
7
8
9
10
11
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>

2.2. 配置 JavaConfig

Java中主要有两个地方关于websocket的配置

  • 一处是实现HandshakeInterceptor,用于在客户端与websocket建立握手和挥手的时候调用的函数
    • 握手前的处理beforeHandshake 可以用来权限校验等
    • 握手后的处理afterHandshake
  • 另一处是实现WebSocketHandler,用于在客户端与服务端建立session之后调用的函数,主要包括
    • 连接成功的方法afterConnectionEstablished
    • 处理消息的方法handleMessage
    • 处理异常的方法handleTransportError
    • 连接关闭的方法afterConnectionClosed

我们主要方法是handleMessage处理消息会每次调用,handleTransportError在错误的时候调用,其余的均调用一次

第一次的调用流程为 HandshakeInterceptor#beforeHandshake() -> HandshakeInterceptor#afterHandshake() ->WebSocketHandler#afterConnectionEstablished() ->WebSocketHandler#handleMessage()

2.2.1. 配置HandshakeInterceptor

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

@Component
public class KHandshakeInterceptor implements HandshakeInterceptor {

/**
* 握手前
*/
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
System.out.println("握手开始");
// 获得请求参数
Map<String, String> paramMap = HttpUtil.decodeParamMap(request.getURI().getQuery(), "utf-8");
String uid = paramMap.get("token");
if (StrUtil.isNotBlank(uid)) {
// 放入属性域
attributes.put("token", uid);
System.out.println("用户 token " + uid + " 握手成功!");
return true;
}
System.out.println("用户登录已失效");
return false;
}

/**
* 握手后
*/
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
System.out.println("握手完成");
}

}

2.2.2. 配置HandshakeInterceptor

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
58
59
60
61
62
63
64
65
66
67
68
69
@Component
public class KWebSocketHandler extends TextWebSocketHandler {
/**
* 保存连接 session 的地方,模拟用一个Map保存
*/
private static ConcurrentHashMap<String, WebSocketSession> SESSION_POOL = new ConcurrentHashMap<>();

/**
* socket 建立成功事件
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
Object token = session.getAttributes().get("token");
if (token != null) {
// 用户连接成功,放入在线用户缓存
SESSION_POOL.put(token.toString(), session);
} else {
throw new RuntimeException("用户登录已经失效!");
}
}

/**
* 接收消息事件
*/
//@Override
protected void handleTextMessageBak(WebSocketSession session, TextMessage message) throws Exception {
// 获得客户端传来的消息
String payload = message.getPayload();

Object token = session.getAttributes().get("token");
System.out.println("server 接收到 " + token + " 发送的 " + payload);
session.sendMessage(new TextMessage("server 发送给 " + token + " 消息 " + payload + " " + LocalDateTime.now().toString()));
}
/**
* 客户端双向沟通处理消息的事件,拿到另一个user的session,设置值即可
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 获得客户端传来的消息
String payload = message.getPayload();
//user2|你好user2
//简易的处理一下要发送给的另外一个人,用两个人模拟对话
//http://coolaf.com/tool/chattest用这个工具可以快速的发用ws消息
String[] split = payload.split("\\|");
String toUser = split[0];
String msg = split[1];
Object token = session.getAttributes().get("token");
System.out.println("server 接收到 " + token + " 发送的 " + payload);
WebSocketSession toSession = SESSION_POOL.get(toUser);
if (toSession == null) {
session.sendMessage(new TextMessage("用户不在线"));
} else {
toSession.sendMessage(new TextMessage(token+"|给你发来消息:"+msg));
}
}

/**
* socket 断开连接时
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
Object token = session.getAttributes().get("token");
if (token != null) {
// 用户退出,移除缓存
SESSION_POOL.remove(token.toString());
}
}

}

2.2.3. websocket 配置文件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

@Autowired
private KWebSocketHandler socketHandler;
@Autowired
private KHandshakeInterceptor handshakeInterceptor;

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//注册握手处理器和消息处理拦截器
registry
.addHandler(socketHandler, "ws")
.addInterceptors(handshakeInterceptor)
.setAllowedOrigins("*");
}
}

2.2.4. springboot项目启动

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

前端工程搭建

暂时没处理,vue项目参考https://blog.csdn.net/thmail/article/details/104064921