1.网页版即时通讯背后的原理 http协议和服务器推送技术: http协议为无状态,单向性的协议。
单向性:必须由客户端发起的一个请求建立的连接,服务器接收请求,把数据返回给客户端,典型的一请求一响应。无状态:服务器与客户端通过http协议建立连接,当我们浏览器向服务器发送请求然后服务给我们应答,当我们再一次向服务器发送请求时,对于服务器来讲,在它看来这个请求是一个全新的用户。(在http协议中为了解决这种无状态的问题提出了cookie和session)那么基于http协议的特性想简单实现一个在网页上显示服务器上的时间都是个难题。
基于http的解决方案:
ajax短轮询:通过前端不停的定时的向服务器发送请求,服务器收到请求后马上返回相关应答。 缺点:带宽上的浪费。 结果的一个延迟。当我们的客户端向服务器发送请求的时候。服务器没有最新的一个结果,但是服务器得马上给一个应答,那么这次客户端的请求相当于无效请求,那么就得等到客户端进行下一次请求才能拿到数据。
ajax长轮询:当客户端向服务器发送请求时,并不会每一次都马上发生应答,只有当服务器发生数据更新才发生应答,否则会停留一段时间。 缺点:客户端没有数据到达时,http连接会停留一段时间,这会造成服务器资源浪费。
基于长连接的服务器推模型sse:当客户端向服务端发送请求时,当有数据更新时发生应答但是不断开连接,有新数据更新时继续向客户端发送数据。
2.服务器推送之最强武器——WebSocket通信
什么是websocket 也是h5中的协议,实现客户端与服务端双向,基于消息的文本或二进制数据通信。(天生支持发送图片视频)适合于对数据的实时性要求很强的场景,如通信、直播、共享桌面、特别适合于客户于服务频繁交互的情况下、如实时共享、多人协作等平台。采用新协议,后端需要单独实现。客户端并不是所有浏览器都支持。 websocket通信原理 websocket借用了http的协议来完成一部分握手websocket通信——STOMP websocket是个规范,在实际的实现中有html5规范中的websocketapi和websocket的子协议STOMP
STOMP
简单(流)文本定向消息协议STOMP协议的前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计。是属于消息队列的一种协议,它的简单性恰巧可以用于定义websocket的消息体格式,stomp协议很多mq都已支持,比如rabbitmq,activemq。websocket通信实现 结合springboot实现基于stomp的微信风格——群聊 1.sockjs把websocket通讯相关部分进行封装,如果当前浏览器不支持websocket那么将自动降级为ajax轮询。 2.stompjs对stomp进行一个封装。 3.这两个js都需要引入jquery。 1.首先第一步通过sockjs.min.js拿到一个socketjs对象和服务器建立连接,连接的地址就endpoint(指定地址) 2.stomp首先向我们的服务器端发起一个订阅(订阅到应答中转上面) 3.当stomp客户端向服务器端发送消息时,客户端发送的消息经过应答中转拼凑成一个响应报文再发送出去,服务器端会将消息发送到所有订阅了应答中转(mass)的客户端。
编写一个配置文件来开启对stomp协议的支持。
@Configuration /*开启使用Stomp协议来传输基于消息broker的消息 这时控制器支持使用@MessageMapping,就像使用@RequestMapping一样*/ @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { /*注册STOMP协议的节点(endpoint),并映射指定的url, * 添加一个访问端点“/endpointMark”,客户端打开双通道时需要的url, * 允许所有的域名访问,指定使用SockJS协议。*/ registry.addEndpoint("/endpointMark") .setAllowedOrigins("*") .withSockJS(); } //所有订阅了mass方法的客户端都会收到mass方法发送出来的消息 @Override public void configureMessageBroker(MessageBrokerRegistry registry) { /*配置消息代理*/ registry.enableSimpleBroker("/mass","/queue"); /*一对一访问时,给用户信息时增加发送地址的前缀*/ registry.setUserDestinationPrefix("/queue"); } }这里给出示例:在js代码中建立连接
//声明一个对象,作为stomp的客户端 var stompClient = null; //浏览器加载页面后调用connect(),打开websocket通道 $(function(){ //打开双通道 connect() }) //打开通道 function connect(){ //连接SockJS的endpoint名称为"endpointMark" var socket = new SockJS("/endpointMark"); //使用STMOP子协议的WebSocket客户端 stompClient = Stomp.over(socket); //连接WebSocket服务端,后台打印连接信息 stompClient.connect({},function (frame) { console.log("Connected:"+frame); stompTopic();//发起订阅 conName();//获取登录者姓名 stompQueue();//一对一发起订阅 }); }获取姓名 以及发起订阅操作
//获取姓名并拼接上去 function conName() { var response = $("#name_div"); $.ajax({ url:"name", type:"post", dataType : "json", async:false, success:function (data) { var b = JSON.parse(JSON.stringify(data)); response.append("<li class='name' id='name_1'>"+b+"</li>"); } }); } //发起订阅 function stompTopic(){ //通过stompClient.subscribe订阅目标(destination)发送的消息(广播接收信息) stompClient.subscribe('/mass',function (response) { var message=JSON.parse(response.body); //展示广播所接收的内容 var response = $("#mass_div"); var userName=$("#name_div").html(); if(userName==message.fromName){ response.append("<div class='user-group'>" + " <div class='user-msg'>" + " <span class='user-reply'>"+message.chatValue+"</span>" + " <i class='triangle-user'></i>" + " </div>" +message.fromName+ " </div>"); }else{ response.append(" <div class='admin-group'>"+ message.fromName+ "<div class='admin-msg'>"+ " <i class='triangle-admin'></i>"+ " <span class='admin-reply'>"+message.chatValue+"</span>"+ "</div>"+ "</div>"); } }); } //一对一,发起订阅 function stompQueue(){ var userId=$("#name_1").html(); //通过stompClient.subscribe订阅目标(destination)发送的消息(队列接收信息) stompClient.subscribe('/queue/' + userId + '/alone', function(response){ var message=JSON.parse(response.body); //展示一对一的接收的内容接收 var response = $("#alone_div"); response.append(" <div class='admin-group'>"+ message.fromName+ "<div class='admin-msg'>"+ " <i class='triangle-admin'></i>"+ " <span class='admin-reply'>"+message.chatValue+"</span>"+ "</div>"+ "</div>"); }); }订阅完成后我们只需要将我们每次发送的消息拼接到我们的前端界面上。