最近在做一个springboot+websocket的服务,踩了很多坑,查阅了大量资料,在此把前辈的资料和自己的代码整理一份:
1.实现websocket有两种方式,一种是基于h5(后台对应tomcat实现方式),另一种是stomp(socketjs)协议(后台对应spring框架实现方式)
2.其中,Tomcat实现方式,需要Tomcat7.x以上,JEE7的支持;而spring框架实现方式,需要spring版本4.X以上
3.(划重点)目前两种方式并不兼容,所以选择实现方式时,要和前端工程师沟通好
4.对于两种方式优劣,仁者见仁智者见智了,还是要看业务
5.关于websocket介绍和实现方式的原理,本文暂不介绍了
6.附参考资料:(1).https://blog.csdn.net/qq_28988969/article/details/78113463
(2)https://www.cnblogs.com/interdrp/p/7903736.html
org.springframework.boot
spring-boot-starter-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;
@Configuration
@EnableWebSocketMessageBroker
/**
通过EnableWebSocketMessageBroker 开启使用STOMP协议来传输基于代理(message broker)的消息,
此时浏览器支持使用@MessageMapping 就像支持@RequestMapping一样。
*/
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// 允许使用socketJs方式访问,访问点为/websocket/server,允许跨域
registry.addEndpoint("/websocket/server").setAllowedOrigins("*").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//用户订阅主题的前缀 /topic 代表发布广播,即群发,若要实现点对点发送,后面会有介绍
registry.enableSimpleBroker("/topic");
}
}
备注:网上关于配置的方法还有
extends AbstractWebSocketMessageBrokerConfigurer
等过时方法,本文给出的是最新方法,功能是一样的。
客户端发送服务端实体类,这个就很随意了,可以自定义
public class Message {
private String name;
private String retailmId;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRetailmId() {
return retailmId;
}
public void setRetailmId(String retailmId) {
this.retailmId = retailmId;
}
@Override
public String toString() {
return "Message{" +
"name='" + name + '\'' +
", retailmId='" + retailmId + '\'' +
'}';
}
}
import com.koolearn.bean.Message;
import com.koolearn.bean.Response;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 〈一句话功能简述〉
* 〈这是一个例子〉
*
* @author LiYuAn
* @create 2018/11/5
* @since 1.0.0
*/
@Api(value = "websocket推送接口Demo", tags = "websocket推送接口Demo")
@Controller
public class DemoController {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@MessageMapping(value = "/welcome")
public void userChat(Message message) throws Exception {
System.out.println(message.toString());
String url = "/topic/getResponse/"+message.getRetailmId();
messagingTemplate.convertAndSend(url, new Response("welcome!","1"));
}
@MessageMapping("/sendTest")
@SendTo("/topic/subscribeTest")
public ServerMessage sendDemo(ClientMessage message) {
logger.info("接收到了信息" + message.getName());
return new ServerMessage("你发送的消息为:" + message.getName());
}
@SubscribeMapping("/topic/getResponse")
public Response sub() {
return new Response("","感谢您订阅了我");
}
}
代码详解:
1.@Api
注解是本项目集成了swagger功能,若不集成,可以删掉;集成方法很简单,百度即可。。。。
2.@MessageMapping("/sendTest")
(1)订阅/sendTest地址,即客户端发送地址为:服务器ip+/websocket/server/sendTest时,通过该注解接受客户端发送的消息,在方法里处理业务, @MessageMapping功能和@requestMapping类似;
(2)当和@SendTo("/topic/subscribeTest")一起使用时,可以实现处理业务后,向订阅了/topic/subscribeTest地址的客户端发送消息;
(3)SimpMessagingTemplate messagingTemplate;这是spring框架的发送模板,和@sendTo功能一样,可实现点对点发送,即在和前端约定好在订阅地址后加动态参数即可,在本文的例子中,前端传入了唯一标识,可以是id、token,loginName等等,后台通过消息类取到:String url = “/topic/getResponse/”+message.getRetailmId();此时,url是唯一的,通过convertAndSend(url,XXX)方法就实现了点对点推送
(4)关于点对点发送,还可以通过@sendToUser和messagingTemplate.convertAndSendToUser(user,url,xxx)实现,具体代码下回见分晓 !
3.@SubscribeMapping("/topic/getResponse")
当客户端订阅了此地址时,交给该注解所在方法处理业务
Spring Boot+WebSocket+广播式(点对点)
说明:
前端用Stomp实现很简单,只要引入两个stomp的js文件即可
如果你的业务更复杂一些,比如A服务想通过http请求调用websoket服务,并向前端推送消息,可以通过@RequestMapping+SimpMessagingTemplate组合实现,返回类型就自定义了
@Api(value = "websocket推送接口", tags = "websocket推送接口")
@Controller
@RequestMapping(value = "/websocket")
public class WebSocketController {
@Autowired
private SimpMessagingTemplate messagingTemplate;
@ApiOperation(value = "消息推送接口")
@RequestMapping(value = "/push", method = RequestMethod.GET)
@ResponseBody
public xxx pushMsg(@RequestParam Integer count,
@RequestParam String 唯一标识 {
String url = "/topic/getResponse/" + 唯一标识;
messagingTemplate.convertAndSend(url, new Response(xxx));
return xxx;
}
}
在项目中,LZ又踩到了一个坑,就是在开发微信公众号时用到了websocket,前端页面是https的,如果你前台发起websocket连接,会有惊喜
如果前台的链接写成htpps,还会报错,那该怎么解决呢?LZ的解决方案是Nginx升级至websocket协议,只需添加配置:
location ^~ /websocket/server {
proxy_pass http://127.0.0.1:项目端口;
proxy_http_version 1.1;
proxy_set_header X-Client-IP $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
需要注意的是,在h5实现的方法里,ws对应http,即ip写成ws://xxx.x.x.x…;wss对应https,即wss:xxx.x.x.x…,stomp协议的实现方式正常写就可以了
详情参考http://www.cnblogs.com/zhjh256/p/6262620.html,其他的报错也可参考https://my.oschina.net/robinjiang/blog/898557,貌似前提是你的服务器要配置成支持https的,关于Nginx的使用和配置,这里就暂不介绍了,以后单写一篇
END