在上篇文章中,松哥和大家分享了 WebFlux 的基本用法,小伙伴们已经了解到使用 WebFlux 我们的返回值可以是 Mono 也可以是 Flux,如果是 Flux,由于 Flux 中包含多个元素,所以我们需要设置响应的 Content-Type 为 text/event-stream。考虑到很多小伙伴还没用过 text/event-stream,所以今天松哥再撸一篇文章来和大家聊聊 text/event-stream

1.SSE

首先我们来看一个概念叫做 SSE。

SSE 全称是 Server-Sent Events,它的作用和 WebSocket 的作用相似,都是建立浏览器与服务器之间的通信渠道,然后服务器向浏览器推送信息,不同的是,WebSocket 是一种全双工通信协议,而 SSE 则是一种单工通信协议,即使用 SSE 只能服务器向浏览器推送信息流,浏览器如果向服务器发送信息,就是一个普通的 HTTP 请求。

使用 SSE,当服务端给客户端响应的时候,他不是发送一个一次性数据包,而是会发送一个数据流,这个时候客户端的连接不会关闭,会一直等待服务端发送过来的数据流,我们常见的视频播放其实就是这样的例子。

SSE 和 WebSocket 主要有如下区别:

  • SSE 使用 HTTP 协议,现有的服务器软件都支持。WebSocket 是一个独立协议。

  • SSE 属于轻量级,使用简单;WebSocket 协议相对复杂。

  • SSE 默认支持断线重连,WebSocket 需要自己实现。

  • SSE 一般只用来传送文本,二进制数据需要编码后传送,WebSocket 默认支持传送二进制数据。

  • SSE 支持自定义发送的消息类型。

说了这么多,可能大家还是有点懵,接下来松哥通过一个简单的例子来向大家展示 SSE 的用法。

2.开发服务端

根据第一小节的描述,大家也能看出来,SSE 其实和框架没有关系,所以这里松哥就创建一个普通的 Java Web 项目,用最最基本的 Servlet 来向大家演示 SSE 的功能。

首先我们创建一个 SseServlet,内容如下:

@WebServlet(urlPatterns = "/sse")
public class SseServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req,resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/event-stream;charset=utf-8");PrintWriter out = resp.getWriter();for (int i = 0; i < 10; i++) {out.write("data: 江南一点雨:" + i+"\n\n");out.flush();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

往出写 10 条数据,每写一条就睡眠 1 秒钟。代码并不难,但是这里有几个细节需要注意下:

  1. 响应的 Content-Type 记得设置为 text/event-stream,这是关键。

  2. 每一次发送的信息,由若干个 message 组成,每个 message 之间用 \n\n 分隔,每个 message 内部由若干行组成。在上面的案例中,每一个 for 循环中就是发送一个 message。

  3. 每一行的数据格式是 :[field]: value\n。field 有四种不同取值:

  • data:data 用来表示数据内容,就像我们上面的例子。

  • id:id 相当于是每一条数据的唯一编号,浏览器用 lastEventId 属性读取这个值。一旦连接断线,浏览器会发送一个 HTTP 头,里面包含一个特殊的 Last-Event-ID 头信息,将这个值发送回来,用来帮助服务器端重建连接。因此,这个头信息可以被视为一种同步机制。

  • event:event 字段表示自定义的事件类型,默认是 message 事件。

  • retry:服务器可以用 retry 字段,指定浏览器重新发起连接的时间间隔。

开发完成后,我们启动服务端访问 /sse 接口来看看效果:

可以看到,客户端每隔 1 秒就能收到服务端的数据。

3.开发客户端

前面是一个服务端的案例,接下来我们来看看客户端的案例,新建一个 html 页面,添加如下 js:

var es = new EventSource("/sse");
es.onopen = function (e) {console.log("open")
};
es.onmessage = function (e) {console.log(e.data);
}
es.onerror = function (e) {console.log("error")es.close()
}

关于上面这段代码:

  1. 首先新建一个 EventSource 对象,参数就是服务端的地址。它还有一个可选的参数,可选参数重可以描述是否将 Cookie 一起发送出去 var es = new EventSource("/es", { withCredentials: true });(可在跨域时使用该参数)。

  2. 当建立连接后,就会触发 onopen 函数,当收到服务端发送来的消息,就会触发 onmessage 函数,当连接出错的时候,就会触发 onerror 函数。

  3. es.close 表示关闭 SSE 连接。

这三种类型的事件,我们还可以通过如下方式来定义:

var es = new EventSource("/sse");
es.addEventListener("open", function (e) {console.log("open");
})
es.addEventListener("message", function (e) {console.log(e.data);
})
es.addEventListener("error", function (e) {console.log("error")es.close();
})

效果与上面的一致,我们来看看运行效果图:

消息接收完后,会触发 onerror 事件,此时我们可以关闭 SSE 连接,否则就会从头开始继续接收数据

4.自定义事件

我们也可以自定义 SSE 事件。

先来看服务端如何自定义:

@WebServlet(urlPatterns = "/sse")
public class SseServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/event-stream;charset=utf-8");PrintWriter out = resp.getWriter();for (int i = 0; i < 10; i++) {out.write("event:javaboy\n");out.write("data: 江南一点雨:" + i + "\n\n");out.flush();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

如上,在每一行消息之前添加 out.write("event:javaboy\n"); 表示自定义事件类型,当然我们也可以添加事件 id,方式如下:

@WebServlet(urlPatterns = "/sse")
public class SseServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("text/event-stream;charset=utf-8");PrintWriter out = resp.getWriter();for (int i = 0; i < 10; i++) {out.write("event:javaboy\n");out.write("id:" + i + "\n");out.write("data: 江南一点雨:" + i + "\n\n");out.flush();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

服务端定义完成后,接下来我们再来看看前端该如何接收数据:

var es = new EventSource("/sse");
es.addEventListener("open", function (e) {console.log("open");
})
es.addEventListener("javaboy", function (e) {console.log(e.data, e.lastEventId, e);
})
es.addEventListener("error", function (e) {console.log("error")es.close();
})

此时在 addEventListener 方法中,输入自定义的事件名称,然后在回调函数中处理事件。

可以通过 e.lastEventId 访问到消息的 id。

5.小结

好啦,今天主要通过几个简单的例子向大家展示 text/event-stream 以及 SSE 相关的知识点,相信大家在学完之后对 WebFlux 中返回值为 Flux 的接口会有更深的理解,读完本文,再去看昨天的文章【WebFlux 初体验】,应该会更香。

参考资料:http://www.ruanyifeng.com/blog/2017/05/server-sent_events.html

热门内容:密码打马赛克已经不安全了!这款开源的去“马赛克“工具一秒还原国产最强开源 API 网关,没有之一,不接受任何反驳!限量!Alibaba首发“Java成长笔记”,差距不止一点点世界排名第一的内存数据库,300分钟就能撸出来!
硬核分享,靠这个技术过了阿里二面!
5种分布式事务最终一致性解决方案,一次性说清了!最近面试BAT,整理一份面试资料《Java面试BAT通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

明天见(。・ω・。)ノ♡

服务端主动推送数据,除了 WebSocket 你还能想到啥?相关推荐

  1. flux服务器推消息,服务端主动推送数据,除了 WebSocket 你还能想到啥?

    原标题:服务端主动推送数据,除了 WebSocket 你还能想到啥? 来自公众号: 江南一点雨 在 上篇文章 中,松哥和大家分享了 WebFlux 的基本用法,小伙伴们已经了解到使用 WebFlux ...

  2. 利用mochiweb让服务端主动推送数据至前端页面

    对于智能化监控软件,从wincc等国外的有相当积累的系统,以及国内一些小型的智能化集成软件,通常其监控数据通过前端绑定控件的方式,做到了实时的通讯,通过控件直接和后端服务交互.这种方式可以灵活的组态, ...

  3. 使用SignalR从服务端主动推送警报日志到各种终端(桌面、移动、网页)

    工作上有个业务,.Net Core WebAPI作为服务端,需要将运行过程中产生的日志分类,并实时推送到各种终端进行报警,终端有桌面(WPF).移动(Xamarin.Forms).网站(Angular ...

  4. spring集成mina,包含心跳检测,实现服务端主动推送

    服务端 1.常规的spring工程集成mina时,pom.xml中需要加入如下配置: <dependency><groupId>org.slf4j</groupId> ...

  5. Netty-Server-Hander自定义解码器-服务端主动推送

    netty server端使用自定义解码器,通过存储client连接实现主动推送消息,并发送自定义心跳包 Server端 依赖 <dependency><groupId>io. ...

  6. 服务端如何推送消息给客户端?

    大家好,我是前端西瓜哥,今天带大家了解一下服务端如何推送消息给客户端. 有时候,我们希望服务端能够主动推送一些信息给客户端.但 HTTP 协议只能让客户端发起请求然后服务端响应,而无法让服务端主动去发 ...

  7. ajax轮询模拟websocket,Ajax轮询和SSE服务器推送数据与websocket模式的区别性学习

    我们试想一下我们做个实时聊天的窗口有几种方法? 在我们不刷新页面并且可以试试更新页面内容的方法 你这时候是不是想到了ajax没错确实可以 Ajax轮询 什么是轮询?顾名思义就是我轮着问你,规定一个时间 ...

  8. RestAPI的进化之路,后端MVVM模式或许来临,通过观察者模式,后端收集前端的GET类请求,主动推送数据变更到前端

    RestAPI的进化之路,后端MVVM模式或许来临,通过观察者模式,后端收集前端的GET类请求,主动推送数据变更到前端 最近几年,前端MVVM模式彻底变革了前端的开发模式,那这股火焰会烧到后端嘛? 我 ...

  9. pushlet实现单机-集群服务端消息推送

    一.什么是pushlet? 1.pushlet推送是一种将java后台数据推送到web页面的框架技术,实现了comet. 2.comet是一个用于描述客户端和服务器之间交互的术语,即使用长期保持的ht ...

最新文章

  1. lamda表达式python_Python--13 lambda表达式
  2. [ACM_几何] Pipe
  3. 复合高斯积分(节点数小于等于3的版本Python实现)
  4. Sigmoid是品优函数吗?
  5. 银行登录控件仿制--防钩子,防嗅探
  6. selenium linux 谷歌浏览器,在Linux平台上无法通过Jenkins中的Selenium启动Chrome浏览器...
  7. 用于MyBatis CRUD操作的Spring MVC 3控制器
  8. 移动端Rem之讲解总结
  9. 【Qt】Qt之进程间通信(Windows消息)【转】
  10. 【OpenCV 例程200篇】71. 连续函数的取样
  11. input radio 样式
  12. win7备份工具_u盘系统重装win7旗舰版详细图解教程
  13. 如何通过路由器日志查看路由器是否断网过_如何正确判断宽带问题还是路由器问题?...
  14. 【5G通信】基于matlab 5G通信新型多载波技术GFDM【含Matlab源码 106期】
  15. 你为什么要去博物馆? 我的理由比较另类
  16. mysql及格率公式_关于mysql进行名次的排名和计算及格率的分享
  17. springboot no tests were found
  18. 码住!Elsevier-最全清样/校样(Proof)处理流程
  19. 中文转换为拼音插件guiHz2Py
  20. 从“傻X”到牛X——写在最前面的话

热门文章

  1. jenkins pipeline脚本_Jenkins 创建流水线 (Pipeline) 项目的脚本
  2. 机器学习常见的分类算法的优缺点
  3. springboot 学习笔记(三)
  4. git init 与 git init --bare 区别
  5. Java 常用对象-Date类和Calender类
  6. Sublime Text 3 (含:配置 C# 编译环境)
  7. Web前端开发最佳实践(8):还没有给CSS样式排序?其实你可以更专业一些
  8. Maven使用常用命令
  9. 利用OWC创建图表的完美解决方案
  10. 如何利用pyecharts绘制炫酷的关系网络图?