WebSocket 是一种在单个 TCP 链接上进行全双工通讯的协议javascript
根据这个定义有两个注意的地方:html
协议就是相互通讯的计算机双方必须共同遵照的一组约定。vue
1)HTTP协议基于 TCP 协议,创建连接必须经过三次握手才能发送信息。java
2)http连接分为短连接,长连接,短连接是每次请求都要三次握手才能发送本身的信息。即每个request 对应一个 response。长连接是在必定的期限内保持连接。保持TCP链接不断开。客户端与服务器通讯,必需要有客户端发起而后服务器返回结果。客户端是主动的,服务器是被动的。 jquery
3)WebSocket 他是为了解决客户端发起多个 http 请求到服务器资源浏览器必需要通过长时间的轮训问题而生的,他实现了多路复用,他是全双工通讯。在 webSocket 协议下客服端和浏览器能够同时发送信息。web
@Configuration @EnableWebSocketMessageBroker //经过此注解开启 WebSocket 消息代理 public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { //设置消息代理的前缀 // 若是消息的前缀是 /topic 就会将消息转发给消息代理(broker)再由消息代理转发给全部链接的客户端 config.enableSimpleBroker("/topic"); //客户端接收服务端消息的地址前缀 //配置一个或多个前缀,经过这些前缀过滤出须要被注解方法处理的消息。 // 例如前缀为"/app"的 destination 能够经过 @MessageMapping 注解的方法处理,而其余 destination("/topic","/query") 将被直接交给 broker 处理 config.setApplicationDestinationPrefixes("/app"); //客户端给服务端发消息的地址前缀 } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { //定义一个前缀为 "chat" 的endpoint,并开启 sockJs支持。 // sockJs 能够解决对 WebSocket 的兼容性问题,客户端将经过这里配置的 url 创建 WebSocket 链接 registry.addEndpoint("/chat").withSockJS(); } }
@Controller public class GreetingController { /** * 执行步骤: * 1,由 WebSocketConfig 中的配置,@MessageMapping 注解接收 "/app/hello" 路径发来的消息 * 2,注解方法对消息进行处理后,将消息转发到 @SendTo 定义的路径上 * 3,@SendTo 定义的路径是一个前缀为 "/topic" 的路径,由配置文件,此消息将被交给消息代理 broker,由 broker 进行广播 * @param message * @return */ @MessageMapping("/hello") @SendTo("/topic/greetings") public Message greeting(Message message) { return message; } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>群聊</title> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/sockjs-client/sockjs.min.js"></script> <script src="/webjars/stomp-websocket/stomp.min.js"></script> <script src="/app.js"></script> </head> <body> <div> <label for="name">请输入用户名</label> <input type="text" id="name" placeholder="用户名"> </div> <div> <button id="connect"type="button">链接</button> <button id="disconnect"type="button" disabled="disabled">断开链接</button> </div> <div id="chat" style="display: none;"> <div> <label for="name">请输入聊天内容</label> <input type="text" id="content" placeholder="聊天内容"> </div> <button id="send" type="button">发送</button> <div id="greetings"> <div id="conversation" style="display: none">群聊进行中</div> </div> </div> </body> </html>
var stompClient = null; //页面显示设置 function setConnected(connected) { $("#connect").prop("disabled", connected); $("#disconnect").prop("disabled", !connected); if (connected) { $("#conversation").show(); $("#chat").show(); } else { $("#conversation").hide(); $("#chat").hide(); } $("#greeting").html(""); } //创建一个 WebSocket 链接,创建链接以前必须输入用户名 function connect() { if (!$("#name").val()) { return; } //建立一个 SockeJS 实例 var socket = new SockJS('/chat'); //使用stomp.over方式建立一个stompClient,完成客户端的建立。 stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { //进行页面设置 setConnected(true); //使用 subscribe 方法订阅服务端发送回来的消息,并将服务端发送的消息展现出来 stompClient.subscribe('/topic/greetings', function (greetings) { showGreeting(JSON.parse(greetings.body)); }) }) } //断开 WebSocket 链接 function disconnect() { if (stompClient != null) { stompClient.disconnect(); } setConnected(false) } //发送信息 function sendName() { stompClient.send("/app/hello", {}, JSON.stringify({'name': $('#name').val(), 'content': $('#content').val()})); } //展现信息 function showGreeting(message) { $('#greetings') .append("<div>" + message.name + ":" + message.content + "</div>") } $(function () { //创建链接 $("#connect").click(function () { connect(); }); //断开链接 $("#disconnect").click(function () { disconnect(); }); //发送信息 $("#send").click(function () { sendName(); }); })
1)maven 引入依赖错误(尽可能去 maven 的中央仓库拷贝依赖)spring
2)stomp 协议的引入 浏览器
使用STOMP的好处在于,它彻底就是一种消息队列模式,你可使用生产者与消费者的思想来认识它,发送消息的是生产者,接收消息的是消费者。而消费者能够经过订阅不一样的destination,来得到不一样的推送消息,不须要开发人员去管理这些订阅与推送目的地以前的关系。
案例见spring官网就有一个简单的spring-boot的stomp-demo,若是是基于springboot,你们能够根据spring上面的教程试着去写一个简单的demo。springboot
/** * 1. @MessageMapping("/hello") Spring提供一个 @MessageMapping 注解实现了对 WebScoket 的封装 * 2. SimpMessagingTemplate 是 Spring-WebSocket 内置的一个消息发送的工具 * @param message * @throws Exception */ @Autowired private SimpMessagingTemplate simpMessagingTemplate; @MessageMapping("/hello") public void greeting(Message message) throws Exception{ //使用这个方法进行消息的转发发送 simpMessagingTemplate.convertAndSend("/topic/greetings",message); }
刚刚实现的功能是群发消息,下面看下私聊是如何实现的。点对点通讯须要配置多个用户,咱们用 SpringSecurity 添加两个用户。服务器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() //添加两个用户 admin,sang,密码设为123。 .withUser("admin") .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") .roles("admin") .and() .withUser("sang") .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") .roles("user"); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin().permitAll(); } }
@Configuration @EnableWebSocketMessageBroker //经过此注解开启 WebSocket 消息代理 public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { //客户端接收服务端消息的地址前缀 //在群发的基础上,添加一个客户端接收地址的前缀。 config.enableSimpleBroker("/topic","/queue"); //客户端给服务端发消息的地址前缀 config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { //定义一个前缀为 "chat" 的endpoint,并开启 sockJs支持。 // sockJs 能够解决对 WebSocket 的兼容性问题,客户端将经过这里配置的 url 创建 WebSocket 链接 registry.addEndpoint("/chat").withSockJS(); } }
@Controller public class GreetingController { @Autowired private SimpMessagingTemplate simpMessagingTemplate; //群发消息使用 @SendTo 注解 @MessageMapping("/hello") @SendTo("/topic/greetings") public Message greeting(Message message) throws Exception{ return message; } //点对点发送消息使用 SimpMessagingTemplate 实现 @MessageMapping("/chat") //来自 "/app/chat" 的消息将会被此方法处理 public void chat(Principal principal, Chat chat)throws Exception{ String from = principal.getName(); chat.setFrom(from); simpMessagingTemplate.convertAndSendToUser(chat.getTo(),"/queue/chat",chat); } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>群聊</title> <script src="/webjars/jquery/jquery.min.js"></script> <script src="/webjars/sockjs-client/sockjs.min.js"></script> <script src="/webjars/stomp-websocket/stomp.min.js"></script> <script src="/chat.js"></script> </head> <body> <div> <div id="chatsContent"></div> <div> 请输入聊天内容:<input type="text" id="content" placeholder="聊天内容"> 目标用户:<input type="text" id="to" placeholder="目标用户"> <button type="button" id="send">发送</button> </div> </div> </body> </html>
var stompClient = null; //创建一个 WebSocket 链接,创建链接以前必须输入用户名 function connect() { //建立一个 SockeJS 实例 var socket = new SockJS('/chat'); stompClient = Stomp.over(socket); stompClient.connect({}, function (frame) { //使用 subscribe 方法订阅服务端发送回来的消息,并将服务端发送的消息展现出来 stompClient.subscribe('/user/queue/chat', function (chat) { showGreeting(JSON.parse(chat.body)); }) }) } //发送信息 function sendMsg() { stompClient.send("/app/chat", {}, JSON.stringify({'content': $('#content').val(), 'to': $('#to').val()})); } //展现信息 function showGreeting(message) { $('#chatsContent') .append("<div>" + message.from + ":" + message.content + "</div>") } $(function () { connect(); $('#send').click(function () { sendMsg(); }); })
演示效果时请使用不一样用户登陆的同一浏览器或者不一样浏览器演示