Springboot2.4整合webscoket

1.springboot整合websocket的依赖

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

2.webSocket的作用

WebSocket是基于TCP协议的,它是全双工通信的,服务端可以向客户端发送信息,客户端同样可以向服务器发送指令,常用于聊天应用中。

3.Springboot加载websocket

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {

    /**
     * 注入ServerEndpointExporter,这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
	/**
     * 一个http请求,先走filter,到达servlet后才进行拦截器的处理,
     * 如果我们把cors放在filter里,就可以优先于权限拦截器执行。
     * @return
     */
    @Bean
    public FilterRegistrationBean filter(){
        CorsConfiguration config=new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedMethod("*");
        config.addAllowedHeader("*");
        //When allowCredentials is true, allowedOrigins cannot contain the special value "*“since that cannot be set on the “Access-Control-Allow-Origin” response header.
        // To allow credentials to a set of origins, list them explicitly or consider using"allowedOriginPatterns” instead.
        config.addAllowedOriginPattern("*");
        UrlBasedCorsConfigurationSource configSource=new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**",config);
        FilterRegistrationBean registrationBean=new FilterRegistrationBean(new CorsFilter(configSource));
        registrationBean.addUrlPatterns("/*");
        //优先级,越低越优先
        registrationBean.setOrder(1);
        return registrationBean;
    }


    @Override
    protected void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                // 设置允许跨域请求的域名
                .allowedOriginPatterns("*")
                .allowedMethods("GET","POST","PUT","DELETE","OPTIONS")
                // 是否允许证书(cookies)
                .allowCredentials(true)
                //设置时间有效
                .maxAge(3600);
    }
}

4.websocket核心代码

4.1.server类

package com.zyp.websocket;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author syl
 * @description TODO
 * @since 2022/3/26
 */
//通过注解ServerEndpoint设置WebSocket连接点的服务地址
@ServerEndpoint(value = "/websocket/{userId}")
/**
 *@Component注解表明一个类会作为组件类,并告知Spring要为这个类创建bean,此处必须要有
 */
@Component
public class WebSocketServer {

    private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);

    /**
     * 在线人数
     */
    private static AtomicInteger count=new AtomicInteger(0);

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;

    /**
     * 使用map对象,便于根据userId来获取对应的WebSocket,或者放redis里面
     */
    private static Map<String,WebSocketServer> webSocketServerMap=new ConcurrentHashMap<>();

    /**
     * 用户id
     */
    private String userId="";

    /**
     * 强制下线指定用户
     * @param userId
     * @return
     */
    public static String offline(String userId) {
        if(webSocketServerMap.containsKey(userId)){
            webSocketServerMap.remove(userId);
            //用户-1
            count.getAndDecrement();
            return "强制下线用户"+userId+"成功";
        }else{
            return "当前用户"+userId+"不在线";
        }
    }

    /**
     * 连接建立成功调用的方法
     * 注意此处是PathParam不是PathVariable
     * @param userId
     */
    @OnOpen
    public void onOpen(@PathParam(value = "userId") String userId,Session session){
        this.userId=userId;
        this.session=session;
        //判断当前用户是否在线
        if(webSocketServerMap.containsKey(userId)){
            webSocketServerMap.remove(userId);
            webSocketServerMap.put(userId,this);
        }else {
            webSocketServerMap.put(userId, this);
            //数量+1
            count.getAndIncrement();
        }
        log.info("websocket新连接:{},当前在线人数为:{}",userId,getOnline());
    }

    /**
     * 该方法在断开连接后调用
     */
    @OnClose
    public void OnClose(){
        //用户在线才断开连接
        if(webSocketServerMap.containsKey(this.userId)){
            webSocketServerMap.remove(this.userId);
            //数量-1
            count.getAndDecrement();
            log.info("websocket连接关闭:{},当前在线人数为:{}",this.userId,getOnline());
        }
    }

    /**
     * 该方法在连接异常调用
     */
    @OnError
    public void OnError(Throwable throwable){
        log.info("websocket连接关闭:{},错误原因为:{}",this.userId,throwable.getMessage());
        webSocketServerMap.remove(this.userId);
        //数量-1
        count.getAndDecrement();
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void OnMessage(String message,Session session){
        log.info("收到来自窗口"+userId+"的信息:"+message);
        if(StringUtils.isNotBlank(message)){
            JSONArray list= JSONArray.parseArray(message);
            for (int i = 0; i < list.size(); i++) {
                try {
                    //解析发送的报文
                    JSONObject object = list.getJSONObject(i);
                    String toUserId=object.getString("toUserId");
                    String contentText=object.getString("contentText");
                    object.put("fromUserId",this.userId);
                    //传送给对应用户的websocket
                    if(StringUtils.isNotBlank(toUserId)&&StringUtils.isNotBlank(contentText)){
                        WebSocketServer webSocketServer = webSocketServerMap.get(toUserId);
                        //需要进行转换,userId
                        if(webSocketServer!=null){
//                            webSocketServer.sendMessage(JSON.toJSONString(ApiReturnUtil.success(object)));
                            //此处可以放置相关业务代码,例如存储到数据库
                        }
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 实现服务器主动发送信息
     * @param message
     * @param message
     */
    public void sendMessage(String message){
        //异步发送
        this.session.getAsyncRemote().sendText(message);
    }

    /**
     * 向指定的用户发送信息
     * @param message
     * @param message
     */
    public static void sendInfo(String userId,String message){
        if(webSocketServerMap.containsKey(userId)){
            webSocketServerMap.get(userId).sendMessage(message);
            log.info("发送给用户:{}的信息:{}成功",userId,message);
        }else {
            log.info("用户:{}不在线",userId);
        }
    }

    /**
     * 群发信息
     * @param message
     * @param userIds
     */
    public static void batchSendInfo(String message, List<String> userIds){
        if(CollectionUtils.isEmpty(userIds)){
            webSocketServerMap.keySet().forEach(userId->sendInfo(userId,message));
        }else {
            userIds.forEach(userId->sendInfo(userId, message));
        }

    }


    /**
     * 获取在线人数
     * @return
     */
    public static int getOnline(){
        return count.intValue();
    }

    /**
     * 获取在线用户
     * @return
     */
    public static Set<String> getUser(){
        return webSocketServerMap.keySet();
    }


}

4.2.控制层

package com.zyp.controller;

import com.google.common.collect.Lists;
import com.zyp.common.NoLogin;
import com.zyp.websocket.WebSocketServer;
import com.zyp.util.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;
import java.util.Set;

/**
 * @author syl
 * @description 测试websocket的使用
 * @since 2022/3/26
 */
@RestController
@RequestMapping(value = "websocket/")
@Api(tags = "websocket测试")
public class WebSocketController {

    @ApiOperation("发送信息")
    @GetMapping("pushMessage/{userId}")
    @NoLogin
    public Result pushMessage(@PathVariable String userId, @RequestParam String message){
        if(StringUtils.equals(userId, "all")){
            WebSocketServer.batchSendInfo(message, Lists.newArrayList());
        }else{
            WebSocketServer.batchSendInfo(message, Arrays.asList(userId.split(",")));
        }
        return Result.ok("发送成功");
    }

    @ApiOperation("获取在线的")
    @GetMapping("getOnline")
    @NoLogin
    public Result getOnline(){
        //在线人数
        int count = WebSocketServer.getOnline();
        Set<String> users = WebSocketServer.getUser();
        return Result.ok().put("count",count).put("userList",users);
    }

    @ApiOperation("强制下线指定用户")
    @GetMapping("offline/{userId}")
    @NoLogin
    public Result offline(@PathVariable String userId){
        String result = WebSocketServer.offline(userId);
        return Result.ok(result);
    }
}

4.3前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket</title>
</head>

<body>
<div id="message"></div>
</body>

<script>
    let websocket = null;

    // 用时间戳模拟登录用户
    const username = new Date().getTime();
    // alert(username)
    //判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        console.log("浏览器支持Websocket");
        websocket = new WebSocket('ws://localhost:9002/websocket/' + username);
    } else {
        alert('当前浏览器 不支持 websocket');
    }

    //连接发生错误的回调方法
    websocket.onerror = function () {
        setMessageInnerHTML("WebSocket连接发生错误");
    };

    //连接成功建立的回调方法
    websocket.onopen = function () {
        setMessageInnerHTML("WebSocket连接成功");
    };

    //接收到消息的回调方法
    websocket.onmessage = function (event) {
        setMessageInnerHTML(event.data);
    };

    //连接关闭的回调方法
    websocket.onclose = function () {
        setMessageInnerHTML("WebSocket连接关闭");
    };

    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function () {
        closeWebSocket();
    };

    //关闭WebSocket连接
    function closeWebSocket() {
        websocket.close();
    }

    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML) {
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }
</script>
</html>
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值