webscoket原理:请参考WebSocket的实现原理
webscoket一开始我只是简单会用,但是我觉得掌握webscoket原理是很有必要,他会加深我们对计网的理解。
一、永恒第一步:导入pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
二、添加配置文件
对应部分的说明都写在了注释里面
package com.easy.config;
import com.mbyte.easy.webscoket.consts.GlobalConsts;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
/**
* @Author zte
* @Description Webscoket配置类
* @Date 21:33 2019/5/6
**/
@Configuration
@EnableWebSocketMessageBroker
@EnableCaching
@CrossOrigin("*")
public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
/**
* 配置消息代理
* 启动简单Broker,消息的发送的地址符合配置的前缀来的消息才发送到这个broker
*/
config.enableSimpleBroker(GlobalConsts.TOPICPATH, GlobalConsts.P2PPUSHBASEPATH);
config.setUserDestinationPrefix(GlobalConsts.P2PPUSHBASEPATH);
config.setApplicationDestinationPrefixes(GlobalConsts.APP_PREFIX);
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
/**
* 注册 Stomp的端点
* addEndpoint:添加STOMP协议的端点。这个HTTP URL是供WebSocket或SockJS客户端访问的地址
* setAllowedOrigins("*") 允许跨域
* withSockJS:指定端点使用SockJS协议
*/
registry.addEndpoint(GlobalConsts.ENDPOINT)
.setAllowedOrigins("*")
.withSockJS();
}
}
如果需要跨域,则需要添加跨域设置
package com.easy.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import javax.annotation.Resource;
/**
* @className: WebSocketConfig
* @description:
* @author: zte
* @create: 2020-06-09 16:17
**/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Resource
private MyHandShakeInterceptor handshake;
@Resource
private MyHandler handler;
/**
* 实现 WebSocketConfigurer 接口,重写 registerWebSocketHandlers 方法,这是一个核心实现方法,配置 websocket 入口,允许访问的域、注册 Handler、SockJs 支持和拦截器。
* <p>
* registry.addHandler()注册和路由的功能,当客户端发起 websocket 连接,把 /path 交给对应的 handler 处理,而不实现具体的业务逻辑,可以理解为收集和任务分发中心。
* <p>
* addInterceptors,顾名思义就是为 handler 添加拦截器,可以在调用 handler 前后加入我们自己的逻辑代码。
* <p>
* setAllowedOrigins(String[] domains),允许指定的域名或 IP (含端口号)建立长连接,如果只允许自家域名访问,这里轻松设置。如果不限时使用”*”号,如果指定了域名,则必须要以 http 或 https 开头。
*
*/
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//部分 支持websocket 的访问链接,允许跨域
registry.addHandler(handler, "/websocket").addInterceptors(handshake).setAllowedOrigins("*");
//部分 不支持websocket的访问链接,允许跨域
// registry.addHandler(handler, "/sockjs/echo").addInterceptors(handshake).setAllowedOrigins("*").withSockJS();
}
}
三、WebScoket常量配置
package com.easy.webscoket.consts;
/**
* @program: easy
* @description: WebScoket常量配置
* @author: zte
* @create: 2019-05-06 10:11
**/
public class GlobalConsts {
public static final String TOPIC = "/topic/greetings";
public static final String ENDPOINT = "/gs-guide-websocket";
public static final String APP_PREFIX = "/app";
public static final String HELLO_MAPPING = "/hello";
//点对点消息推送地址前缀
public static final String P2PPUSHBASEPATH = "/user";
//点对点消息推送地址后缀,最后的地址为/user/用户识别码/msg
public static final String P2PPUSHPATH = "/msg";
/**
* @Description 接收消息地址
**/
public static final String RECEIVE_MAPPING = "/receive";
/**
* @Description 订阅消息推送地址前缀
**/
public static final String TOPICPATH = "/group";
/**
* 统一前缀
*/
public static final String URL_PREFIX = "/rest/";
}
四、controller层代码
这里包含了点对点聊天以及群聊的配置
package com.easy.webscoket.controller;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.mbyte.easy.common.web.AjaxResult;
import com.mbyte.easy.detailed_info_log.entity.DetailedInfoLog;
import com.mbyte.easy.talk_socket.entity.Message;
import com.mbyte.easy.talk_socket.service.IMessageService;
import com.mbyte.easy.webscoket.consts.GlobalConsts;
import com.mbyte.easy.webscoket.vo.ClientMessage;
import com.mbyte.easy.webscoket.vo.ServerMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.util.HtmlUtils;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import java.awt.event.MouseWheelEvent;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @program: easy
* @description: webscoket测试controller
* @author: zte
* @create: 2019-05-06 10:26
**/
@Controller
@RequestMapping("/audience/index")
public class GreetingController {
@Autowired
private SimpMessagingTemplate template;
@Autowired
private IMessageService messageService;
/**
* 在线用户的Map集合,key:用户名,value:Session对象
*/
private static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("username") String username) {
//在webSocketMap新增上线用户
sessionMap.put(username, session);
System.out.println(username);
}
@RequestMapping
public String index(Model model) {
return "webscoket/greeting";
}
@RequestMapping("/index2")
public String index2(Model model) {
return "webscoket/greeting2";
}
@MessageMapping(GlobalConsts.HELLO_MAPPING)
@SendTo(GlobalConsts.TOPIC)
public ServerMessage greeting(ClientMessage message) throws Exception {
// 模拟延时,以便测试客户端是否在异步工作
// Thread.sleep(1000);
template.convertAndSendToUser(message.getId() + "", GlobalConsts.P2PPUSHPATH, JSON.toJSON(new ServerMessage("Hello, " + HtmlUtils.htmlEscape(message.getName()