SpringBoot集成WebSocket实现前后端消息互传的⽅法
什么是WebSocket?
WebSocket 协议是基于 TCP 的⼀种新的⽹络协议。它实现了浏览器与服务器全双⼯ (full-duplex) 通信—允许服务器主动发送信息给客户端。
为什么需要WebSocket?
⼤家都知道以前客户端想知道服务端的处理进度,要不停地使⽤ Ajax 进⾏轮询,让浏览器隔个⼏秒就向服务器发⼀次请求,这对服务器压⼒较⼤。另外⼀种轮询就是采⽤ long poll 的⽅式,这就跟打电话差不多,没收到消息就⼀直不挂电话,也就是说,客户端发起连接后,如果没消息,就⼀直不返回 response 给客户端,连接阶段⼀直是阻塞的。
⽽ WebSocket 解决了 HTTP 的这⼏个难题。当服务器完成协议升级后( HTTP -> WebSocket ),服务端可以主动推送信息给客户端,解决了轮询造成的同步延迟问题。由于 WebSocket 只需要⼀次HTTP 握⼿,服务端就能⼀直与客户端保持通信,直到关闭连接,这样就解决了服务器需要反复解析 HTTP 协议,减少了资源的开销。
现在通过 SpringBoot 集成 WebSocket 来实现前后端通信。
集成 WebSocket 实现前后端通信
项⽬代码结构图
依赖导⼊
SpringBoot2.0 对 WebSocket 的⽀持简直太棒了,直接就有包可以引⼊。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置 WebSocketConfig
springboot其实就是spring启⽤WebSocket的⽀持也是很简单,将ServerEndpointExporter对象注⼊到容器中。
package com.figuration;
import t.annotation.Bean;
import t.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
WebSocket 服务端 WebSocketServer
因为 WebSocket 是类似客户端服务端的形式(采⽤ws协议),那么这⾥的 WebSocketServer 其实就相当于⼀个 ws协议的 Controller。直接 @ServerEndpoint("/websocket") 、@Component 启⽤即可,然后在⾥⾯实现@OnOpen , @onClose ,@onMessage等⽅法
package com.ller;
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 urrent.CopyOnWriteArraySet;
@Component
@ServerEndpoint("/websocket/{sid}")
@Slf4j
public class WebSocketServer {
/
**
* 静态变量,⽤来记录当前在线连接数。应该把它设计成线程安全的。
*/
private static int onlineCount = 0;
/**
* concurrent包的线程安全Set,⽤来存放每个客户端对应的MyWebSocket对象。
*/
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 接收sid
*/
private String sid="";
/**
* 连接建⽴成功调⽤的⽅法
**/
@OnOpen
public void onOpen(Session session,@PathParam("sid") String sid) {
this.session = session;
/
/加⼊set中
webSocketSet.add(this);
//在线数加1
addOnlineCount();
log.info("有新窗⼝开始监听:"+sid+",当前在线⼈数为" + getOnlineCount());
this.sid=sid;
try {
sendMessage("连接成功");
} catch (IOException e) {
<("websocket IO异常");
}
}
/**
* 连接关闭调⽤的⽅法
*/
@OnClose
public void onClose() {
//从set中删除
//在线数减1
subOnlineCount();
log.info("有⼀连接关闭!当前在线⼈数为" + getOnlineCount());
}
/**
* 收到客户端消息后调⽤的⽅法
* @param message 客户端发送过来的消息
**/
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到来⾃窗⼝"+sid+"的信息:"+message);
//发消息
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
<("发⽣错误");
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
BasicRemote().sendText(message);
}
/**
* 发⾃定义消息
* */
public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
log.info("推送消息到窗⼝"+sid+",推送内容:"+message);
for (WebSocketServer item : webSocketSet) {
try {
//这⾥可以设定只推送给这个sid的,为null则全部推送
if(sid==null) {
item.sendMessage(message);
}else if(item.sid.equals(sid)){
item.sendMessage(message);
}
} catch (IOException e) {
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
}
public static synchronized void subOnlineCount() {
}
}
消息推送
⾄于推送新信息,可以再⾃⼰的 Controller 写个⽅法调⽤ WebSocketServer.sendInfo() 即可package com.ller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
@RestController
@RequestMapping("/checkcenter")
public class CheckCenterController {
/**
* 页⾯请求
* @param cid
* @return
*/
@GetMapping("/socket/{cid}")
public ModelAndView socket(@PathVariable String cid) {
ModelAndView mav=new ModelAndView("/socket");
mav.addObject("cid", cid);
return mav;
}
/**
* 推送数据接⼝
* @param cid
* @param message
* @return
*/
@ResponseBody
@RequestMapping("/socket/push/{cid}")
public String pushToWeb(@PathVariable String cid,String message) {
try {
WebSocketServer.sendInfo(message,cid);
} catch (IOException e) {
e.printStackTrace();
return "error:"+cid+"#"+e.getMessage();
}
return "success:"+cid;
}
}
页⾯发起socket请求
然后在页⾯⽤js代码调⽤ socket,当然,太古⽼的浏览器是不⾏的,⼀般新的浏览器或者⾕歌浏览器是没问题的。还有⼀点,记得协议是ws的哦。直接在浏览器控制台开启连接。var socket;
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不⽀持WebSocket");
}else{
console.log("您的浏览器⽀持WebSocket");
//实现化WebSocket对象,指定要连接的服务器地址与端⼝建⽴连接
socket = new WebSocket("ws://localhost:8080/websocket/20");
//打开事件
console.log("Socket 已打开");
//socket.send("这是来⾃客户端的消息" + location.href + new Date());
};
/
/获得消息事件
console.log(msg.data);
//发现消息进⼊开始处理前端触发逻辑
};
//关闭事件
console.log("Socket已关闭");
};
//发⽣了错误事件
alert("Socket发⽣了错误");
//此时可以尝试刷新页⾯
}
//离开页⾯时,关闭socket
//jquery1.8中已经被废弃,3.0中已经移除
// $(window).unload(function(){
//  socket.close();
//});
}
运⾏效果
现在可以在浏览器开启连接,通过客户端调⽤接⼝服务端就可以向浏览器发送消息。
现在打开两个页⾯开启两个连接:
socket = new WebSocket("ws://localhost:8080/websocket/20") ;
socket = new WebSocket("ws://localhost:8080/websocket/22") ;
向前端推送数据:
localhost:8080/checkcenter/socket/push/20?message=Hello
localhost:8080/checkcenter/socket/push/22?message=HelloWorld
可以看到服务端已经将消息推送给了客户端
⽽客户端也收到了消息
先打开页⾯,指定cid,启⽤socket接收,然后再另⼀个页⾯调⽤刚才Controller封装的推送信息的⽅法到这个cid的socket,即可向前端推送消息。
后续
serverEndpointExporter 错误
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘serverEndpointExporter' defined in class path resource [com/xxx/WebSocketConfig.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateExc 如果 tomcat 部署⼀直报这个错,请移除 WebSocketConfig 中 @Bean ServerEndpointExporter 的注⼊。
ServerEndpointExporter 是由 Spring 官⽅提供的标准实现,⽤于扫描 ServerEndpointConfig 配置类和@ServerEndpoint 注解实例。使⽤规则也很简单:
1、如果使⽤默认的嵌⼊式容器⽐如Tomcat 则必须⼿⼯在上下⽂提供ServerEndpointExporter。
2、如果使⽤外部容器部署war包,则不需要提供提供ServerEndpointExporter,因为此时SpringBoot默认将扫描服务端的⾏为交给外部容器处理,所以线上部署的时候要把WebSocketConfig中这段注⼊
bean的代码注掉。
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。