springboot系列之websocket

springboot系列之websocket(订阅,点对点)

  • 简介
    • springboot中使用websocket
      • 依赖
      • websocket配置类
      • 请求接口
      • 前端代码
      • 模拟登陆
      • 效果图

简介

WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。
HTTP 协议有一个缺陷:通信只能由客户端发起,HTTP 协议做不到服务器主动向客户端推送信息
WebSocket 一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输。只要打开一次连接就可以一直发送数据了

springboot中使用websocket

依赖

		
		
			org.springframework.boot
			spring-boot-starter-websocket
		

websocket配置类

在这里插入代码片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;

/**
 * @auther 高松
 * @DATE 2019/7/26  23:19
 * spring-redis
 */


@Configuration
//注解开启使用STOMP协议来传输基于代理(message broker)的消息,这时控制器支持使用@MessageMapping,就像使用@RequestMapping一样
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    //注册STOMP协议的节点(endpoint),并映射指定的url
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        //注册一个STOMP的endpoint,并指定使用SockJS协议
        registry.addEndpoint("/endpointOyzc").setAllowedOrigins("*").withSockJS();
    }

    @Override
    //配置消息代理(Message Broker)
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        //点对点应配置一个/user消息代理,广播式应配置一个/topic消息代理
        registry.enableSimpleBroker("/topic", "/user");
     //点对点使用的订阅前缀(客户端订阅路径上会体现出来),不设置的话,默认也是/user/
        registry.setUserDestinationPrefix("/user");
    }

}

请求接口

import com.example.springredis.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.UUID;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * @auther 高松
 * @DATE 2019/7/22  0:07
 * spring-redis
 */
@RestController
public class UserController {
    @Autowired
    private SimpMessagingTemplate template;
    private ConcurrentSkipListSet concurrentSkipListSet = new ConcurrentSkipListSet();

    @RequestMapping("/login")
    public String login(HttpSession session, HttpServletResponse httpServletResponse) {
        Cookie uid1 = new Cookie("uid", session.getId());
        UUID uuid = UUID.randomUUID();
        httpServletResponse.addCookie(uid1);
        httpServletResponse.addHeader("token",uuid.toString());
        session.setAttribute("token",  uuid.toString());
        concurrentSkipListSet.add(uuid.toString());
        return uuid.toString();
    }

    @RequestMapping("test1")
    public String trest1(HttpSession session,HttpServletRequest httpServletRequest){
        System.out.println(httpServletRequest.getHeader("token"));
        if(concurrentSkipListSet.contains(httpServletRequest.getHeader("token"))){
            template.convertAndSendToUser( httpServletRequest.getHeader("token"),"/test1","我是点对点消息");
            template.convertAndSend("/topic/test1","我是广播的消息");
            return "发送成功";
        }
        return  "请重新登录";
    }
}

前端代码

  • 连接服务端并订阅相关服务
 initWebSocket() {
        this.connection();
        let that = this; // 断开重连机制,尝试发送消息,捕获异常发生时重连
        /*this.timer = setInterval(() => {
          try {
            that.stompClient.send("test");
          } catch (err) {
            console.log("断线了: " + err);
            that.connection();
          }
        }, 5000);*/
      },


      connection() {
        // 建立连接对象
        let socket = new SockJS('http://127.0.0.1:8081/endpointOyzc');
        // 获取STOMP子协议的客户端对象
        this.stompClient = Stomp.over(socket);
        // 定义客户端的认证信息,按需求配置
        let headers = {Authorization: 'gsong'}
        // 向服务器发起websocket连接
        debugger
        let token = localStorage.getItem("token");
        this.stompClient.connect({}, () => {
          this.stompClient.subscribe('/user/'+token+'/test1', (msg) => {
            // 订阅服务端提供的某个topic
            console.log('点对点订阅成功')
            console.log(msg);
            // msg.body存放的是服务端发送给我们的信息
          }, headers);
          this.stompClient.subscribe('/topic/test1', (msg) => {
            // 订阅服务端提供的某个topic
            console.log('广播成功')
            console.log(msg);
            // msg.body存放的是服务端发送给我们的信息
          }, headers);
        //  this.stompClient.send("/app/chat.addUser", headers, JSON.stringify({sender: '', chatType: 'JOIN'}),)
          //用户加入接口
        }, (err) => {
          // 连接发生错误时的处理函数
          console.log('失败')
          console.log(err);
        });
      },
      //连接 后台
      disconnect() {
        if (this.stompClient) {
          this.stompClient.disconnect();
        }
      }, // 断开连接
  • 在前端请求时传入发送地址
// 引入模块
import axios from "axios"
import qs from 'qs'

// 是否允许跨域
axios.defaults.withCredentials=true;

// axios初始化:延迟时间,主路由地址
let instance = axios.create({
  baseURL: 'http://localhost:8081/',
  timeout: 10,
/*  headers :{
    'Content-Type': ' Access-Control-Allow-Origin'
  }*/
});

// 设置拦截器
instance.interceptors.request.use(function(config){
  //在发送请求之前做某事
  let token = localStorage.getItem("token");
  config.headers.token=token;
  return config;
},function(error){
  //请求错误时做些事
  return Promise.reject(error);
});
//响应拦截器
instance.interceptors.response.use(function(response){
  //对响应数据做些事
  return response;
},function(error){
  //请求错误时做些事
  return Promise.reject(error);
});

// 是否销毁拦截器
// 1.给拦截器起个名称 var myInterceptors = instance.interceptors.requesst.use();
// 2.instance.interceptors.request.eject(myInterceptor);

// 请求成功的回调
function checkStatus(res) {
  //请求结束成功
  if (res.status === 200 || res.status === 304) {
    return res.data
  }
  return {
    code: 0,
    msg: res.data.msg || res.statusText,
    data: res.statusText
  }
  return res
}
// 请求失败的回调
function checkCode(res) {
  if (res.code === 0) {
    throw new Error(res.msg)
  }
  return res
}
//模块化导出
export default {
  get(url, params) {
    if (!url) return;
    return instance({
      method: 'get',
      url: url,
      params,
      timeout: 30000
    }).then(checkStatus).then(checkCode)
  },
  post(url, data) {
    if (!url) return;
    return instance({
      method: 'post',
      url: url,
      data: qs.stringify(data),
      timeout: 30000
    }).then(checkStatus).then(checkCode)
  },
  postFile(url, data) {
    if (!url) return;
    return instance({
      method: 'post',
      url: url,
      data
    }).then(checkStatus).then(checkCode)
  }
}

模拟登陆


      test(){
        this.$http.get("test1",{})
          .then(res => {
            console.log(res);//获取数据
          })
          .catch(error => {
            console.log(error);
          })
      },
      login(){
        debugger
        this.$http.get("login",{})
          .then(res => {
            console.log(res);//获取数据
            localStorage.setItem("token",res)
            this.initWebSocket();
          })
          .catch(error => {
            console.log(error);
          })
      },

效果图

开了两个浏览器,下图是连接输出信息
springboot系列之websocket_第1张图片
下图中点击右边的获取信息按钮,这时后台会发送,广播类型和点对点类型,由于是右边点击的所以右图会受到两种类型消息,左边只能收到一种类型的消息

springboot系列之websocket_第2张图片

源码地址:
后端:
链接:https://pan.baidu.com/s/1NPE7ag5geqQqLX1TYEe_Fg
提取码:7ui6
前端:
链接:https://pan.baidu.com/s/1AKnAGbCLB8ZyU-Xud2Ss-w
提取码:j3wt

你可能感兴趣的:(点对点))