SpringBoot2.0集成WebSocket,实现后台向前端推送信息SpringBoot+WebSocket集成
什么是WebSocket?
WebSocket协议是基于TCP的⼀种新的⽹络协议。它实现了浏览器与服务器全双⼯(full-duplex)通信——允许服务器主动发送信息给客户端。
为什么需要 WebSocket?
初次接触 WebSocket 的⼈,都会问同样的问题:我们已经有了 HTTP 协议,为什么还需要另⼀个协议?它能带来什么好处?
答案很简单,因为 HTTP 协议有⼀个缺陷:通信只能由客户端发起,HTTP 协议做不到服务器主动向客户端推送信息。
举例来说,我们想要查询当前的排队情况,只能是页⾯轮询向服务器发出请求,服务器返回查询结果。
轮询的效率低,⾮常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。因此WebSocket 就是这样发明的。
前⾔
2020-10-20 教程补充:
补充关于@Component和@ServerEndpoint关于是否单例模式等的解答,感谢⼤家热⼼提问和研究。
Vue版本的websocket连接⽅法
2020-01-05 教程补充:
整合了IM相关的优化
优化开启/关闭连接的处理
上传到开源项⽬,⽅便⼤家。
感谢⼤家的⽀持和留⾔,14W访问量是满满的动⼒!接下来还会有websocket+redis集优化篇针对多ws服务器做简单优化处理,敬请期待!
话不多说,马上进⼊⼲货时刻。
maven依赖
SpringBoot2.0对WebSocket的⽀持简直太棒了,直接就有包可以引⼊
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
WebSocketConfig
启⽤WebSocket的⽀持也是很简单,⼏句代码搞定
import t.annotation.Bean;
import t.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* 开启WebSocket⽀持
* @author zhengkai.blog.csdn
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
WebSocketServer
这就是重点了,核⼼都在这⾥。
1. 因为WebSocket是类似客户端服务端的形式(采⽤ws协议),那么这⾥的WebSocketServer其实就相当于⼀个ws协议的Controller
2. 直接@ServerEndpoint("/imserver/{userId}") 、@Component启⽤即可,然后在⾥⾯实现@OnOpen开启连接,@onClose关闭连
接,@onMessage接收消息等⽅法。
3. 新建⼀个ConcurrentHashMap webSocketMap ⽤于接收当前userId的WebSocket,⽅便IM之间对userId进⾏推送消息。单机版实
现到这⾥就可以。
4. 集版(多个ws节点)还需要借助mysql或者redis等进⾏处理,改造对应的sendMessage⽅法即可。
package com.softdev.fig;
package com.softdev.fig;
import java.io.IOException;
import urrent.ConcurrentHashMap;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apachemons.lang.StringUtils;
import org.springframework.stereotype.Component;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
/**
* @author zhengkai.blog.csdn
*/
@ServerEndpoint("/imserver/{userId}")
@Component
public class WebSocketServer {
static Log (WebSocketServer.class);
/**静态变量,⽤来记录当前在线连接数。应该把它设计成线程安全的。*/
private static int onlineCount =0;
/**concurrent包的线程安全Set,⽤来存放每个客户端对应的MyWebSocket对象。*/
private static ConcurrentHashMap<String,WebSocketServer> webSocketMap =new ConcurrentHashMap<>(); /**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
private Session session;
/**接收userId*/
private String userId="";
/**
* 连接建⽴成功调⽤的⽅法*/
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId){
this.session = session;
this.userId=userId;
ainsKey(userId)){
webSocketMap.put(userId,this);
//加⼊set中
}else{
webSocketMap.put(userId,this);
//加⼊set中
addOnlineCount();
//在线数加1
}
log.info("⽤户连接:"+userId+",当前在线⼈数为:"+getOnlineCount());
try{
sendMessage("连接成功");
}catch(IOException e){
<("⽤户:"+userId+",⽹络异常");
}
}
/**
* 连接关闭调⽤的⽅法
*/
@OnClose
@OnClose
public void onClose(){
ainsKey(userId)){
//从set中删除
subOnlineCount();
}
log.info("⽤户退出:"+userId+",当前在线⼈数为:"+getOnlineCount());
}
/**
* 收到客户端消息后调⽤的⽅法
*
* @param message 客户端发送过来的消息*/
springboot aop
@OnMessage
public void onMessage(String message, Session session){
log.info("⽤户消息:"+userId+",报⽂:"+message);
//可以发消息
//消息保存到数据库、redis
if(StringUtils.isNotBlank(message)){
try{
//解析发送的报⽂
JSONObject jsonObject = JSON.parseObject(message);
//追加发送⼈(防⽌串改)
jsonObject.put("fromUserId",this.userId);
String String("toUserId");
//传送给对应toUserId⽤户的websocket
if(StringUtils.isNotBlank(toUserId)&&ainsKey(toUserId)){
<(toUserId).JSONString());
}else{
<("请求的userId:"+toUserId+"不在该服务器上");
/
/否则不在这个服务器上,发送到mysql或者redis
}
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error){
<("⽤户错误:"+this.userId+",原因:"+Message());
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message)throws IOException {
BasicRemote().sendText(message);
}
/**
* 发送⾃定义消息
* */
public static void sendInfo(String message,@PathParam("userId") String userId)throws IOException {        log.info("发送消息到:"+userId+",报⽂:"+message);
if(StringUtils.isNotBlank(userId)&&ainsKey(userId)){
<(userId).sendMessage(message);
}else{
<("⽤户"+userId+",不在线!");
}
}
}
public static synchronized int getOnlineCount(){
return onlineCount;
}
public static synchronized void addOnlineCount(){
}
public static synchronized void subOnlineCount(){
}
}
消息推送
⾄于推送新信息,可以再⾃⼰的Controller写个⽅法调⽤WebSocketServer.sendInfo();即可
import com.softdev.fig.WebSocketServer;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;
/**
* WebSocketController
* @author zhengkai.blog.csdn
*/
@RestController
public class DemoController {
@GetMapping("index")
public ResponseEntity<String>index(){
return ResponseEntity.ok("请求成功");
}
@GetMapping("page")
public ModelAndView page(){
return new ModelAndView("websocket");
}
@RequestMapping("/push/{toUserId}")
public ResponseEntity<String>pushToWeb(String message,@PathVariable String toUserId)throws IOException {
WebSocketServer.sendInfo(message,toUserId);
return ResponseEntity.ok("MSG SEND SUCCESS");
}
}
页⾯发起
页⾯⽤js代码调⽤websocket,当然,太古⽼的浏览器是不⾏的,⼀般新的浏览器或者⾕歌浏览器是没问题的。还有⼀点,记得协议是ws的,如果使⽤了⼀些路径类,可以replace(“http”,“ws”)来替换协议。

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