0%

Springboot集成WebSocket功能

Springboot集成WebSocket功能


由于MT管理器论坛需要添加聊天功能,在网上搜了很多,最后发现了websocket可以用于实时通信和聊天室功能,然后看了慕课上的一个网课,跟着他做出来了一个demo,下面就来看一下什么是websocket吧


菜鸟教程中的解释是这样的

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。
HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
以下 API 用于创建 WebSocket 对象。

简单来说就是一个可以不用使用轮训就可以实现后端主动向前端发送消息的一个协议。现在我们来看一下如何在springboot中实现这个


1.添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>

2.写配置文件

WebSocketConfig.java

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
package xyz.suiwo.websocketdemo.Config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

/**
* 注册端点,发布或者订阅消息的时候需要连接此端点
* setAllowedOrigins 非必须,*表示允许其他域进行连接
* withSockJS 表示开始sockejs支持
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/endpoint-websocket").setAllowedOrigins("*").withSockJS();
}

/**
* 配置消息代理(中介)
* enableSimpleBroker 服务端推送给客户端的路径前缀
* setApplicationDestinationPrefixes 客户端发送数据给服务器端的一个前缀
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/getMessage");
registry.setApplicationDestinationPrefixes("/sendMessage");
}
}

3.消息的实体类

这是我们需要新建一个model用来表示发送的消息类
Message.java

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
package xyz.suiwo.websocketdemo.model;

public class Message {
private String fromUser;
private String toUser;
private String message;

public String getFromUser() {
return fromUser;
}

public void setFromUser(String fromUser) {
this.fromUser = fromUser;
}

public String getToUser() {
return toUser;
}

public void setToUser(String toUser) {
this.toUser = toUser;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

@Override
public String toString() {
return "Message{" +
"fromUser='" + fromUser + '\'' +
", toUser='" + toUser + '\'' +
", message='" + message + '\'' +
'}';
}
}

4.controller方法
我们一共需要书写两个controller一个是用来将消息主动推送给前端(被发送方的),还有一个是将消息以treemap的方式发送给前端让前端(当前用户的)展示。
ChatController.java(用于将消息直接展示给前端显示)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package xyz.suiwo.websocketdemo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.TreeMap;

@RestController
public class ChatController {

@RequestMapping(value = "/chatMessage", method = RequestMethod.POST)
public TreeMap<String, String> add(@Autowired HttpServletRequest request) {
TreeMap<String, String> treeMap = new TreeMap<String, String>();
treeMap.put("toUser", request.getParameter("toUser"));
treeMap.put("fromUser", request.getParameter("fromUser"));
treeMap.put("message", request.getParameter("message"));
return treeMap;
}
}

WebSocketController.java(用于将消息主动推送给被发送方)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package xyz.suiwo.websocketdemo.controller;

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;
import xyz.suiwo.websocketdemo.Service.WebSocketService;
import xyz.suiwo.websocketdemo.model.Message;

@Controller
public class WebSocketController {

private WebSocketService webSocketService;

public WebSocketController(WebSocketService webSocketService) {
this.webSocketService = webSocketService;
}

@MessageMapping(value = "/single/chat")
public void sendMessage(Message message){
webSocketService.sendMessageTo(message.getFromUser(), message.getToUser(), message.getMessage());
}
}

4.前端展示

前端的接收我们需要用的socket.js,具体使用方式。。。我也没有仔细看,只是直接把网站提供的前端源码copy过来了,需要的同学可以参考一下
app.js

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
70
71
72
73
74
75
76
77
var stompClient = null;

function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
}
else {
$("#conversation").hide();
}
$("#notice").html("");
}

function connect() {
var from = $("#from").val();
var to = $("#to").val();
var socket = new SockJS('/endpoint-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/getMessage/single/' + from + to, function (result) {
showContent(result.body);
});
});
}

function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}

function sendName() {
var toUser = document.getElementById("to").value;
var fromUser = document.getElementById("from").value;
var message = document.getElementById("content").value;
stompClient.send("/sendMessage/single/chat", {}, JSON.stringify({
'message': $("#content").val(),
'toUser': $("#to").val(),
'fromUser': $("#from").val()
}));
if (window.XMLHttpRequest) {
// IE7+, Firefox, Chrome, Opera, Safari 浏览器执行代码
xmlhttp = new XMLHttpRequest();
}
else {
// IE6, IE5 浏览器执行代码
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("POST", "/chatMessage", false);
xmlhttp.setRequestHeader('content-type', 'application/x-www-form-urlencoded');
//正式发送请求
xmlhttp.send('toUser=' + toUser + '&fromUser=' + fromUser + '&message=' + message);
}

function showContent(body) {
$("#notice").append("<tr><td>" + body + "</td></tr>");
}

$(function () {
$("form").on('submit', function (e) {
e.preventDefault();
});
$("#connect").click(function () {
connect();
});
$("#disconnect").click(function () {
disconnect();
});
$("#send").click(function () {
sendName();
});
});

这个代码我已经放在了GitHub上有需要的同学可以参考一下 传送门

视频教程 传送门