一、概述
提到服务端数据推送,你可以一下子就想到了websocket,websocket是一种全新的协议,随着html5草案的不断完善,越来越多的现代浏览器开始全面支持websocket技术了,它将tcp的socket(套接字)应用在了webpage上,从而使通信双方建立起一个保持在活动状态连接通道。
但你可能不知道,html5中有一个轻量的替代websocket的方案:sse(server-sent events)。
websocket 和 sse 都是传统请求-响应 web 架构的替代方案,但它们不是完全冲突的技术。 - websocket 架构在客户端与服务器之间打开一个套接字,用于实现全双工(双向)通信。 无需发送 get 消息并等待服务器响应,客户端只需监听该套接字,接收服务器更新,并使用收到的数据来发起或支持各种交互。 客户端也可以使用套接字与服务器通信,例如在成功收到更新时发送 ack 消息。 - sse 是一种更简单的标准,是作为 html5 的扩展而开发的。 尽管 sse 支持从服务器向客户端发送异步消息,但客户端无法向服务器发送消息。 对于客户端只需接收从服务器传入的更新的应用程序,sse 的半双工通信模型最适合。 与 websocket 相比,sse 的一个优势是它是基于 http 而运行的,不需要其他组件。
几乎所有现代浏览器都支持 websocket 协议,包括移动浏览器。然而microsoft ie 和 edge不支持sse 但这并不妨碍我们使用sse,毕竟用ie的人还有几个呢?如果是内部使用,为什么不使用更简单的sse呢?
这里讲述如何使用sse建立服务端的推送。
二、服务端
这里我们使用聊天来模拟sse的数据推送。我这里写了几个自定义的对象 - chater对象存储聊天人的信息。 - websseuser是一个存储用户名username和chater对象的map。
2.1 配置
在springboot项目中使用sse,是不需要额外引入依赖的,只需要把spring-boot-starter-web引入即可。也不需要额外的配置。
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>2.2 服务端text/event-stream长连接
要使用sse,首先需要定义一个维持sse长连接的接口地址,就像websocket中定义websocket的端口地址一样,但是sse这里和普通的http没有多大区别,只是响应头是text/event-stream.
示例:
@getmapping(value = "/subscribe", produces = mediatype.text_event_stream_value)
public sseemitter to(httpservletrequest request) {
string username = (string) request.getsession().getattribute("username");
// 超时时间设置为3分钟
sseemitter sseemitter = new sseemitter(180000l);
chater chater = websseuser.getchater(username);
sseemitter.ontimeout(() -> chater.setsseemitter(null));
sseemitter.oncompletion(() -> system.out.println("完成!!!"));
chater.setsseemitter(sseemitter);
return sseemitter;
}这里是springboot应用中使用sse,我定义了/subscribe接口: - produces指定了响应类型text/event-stream - username从session中获取,并获取到聊天对象chater。 - 这里调用时创建sseemitter对象,设置超时时间3分钟,ontimeout超时后清除sseemitter对象,因为sse可以超时重连,超时会再次调用这个接口,就会重新生成sseemitter对象。 - oncompletion完成后逻辑自定义,但是不要清除sseemitter对象,否则会一直重连。
sse调用/subscribe接口接口以后,会一直使用一个请求,类似websocket。
2.3 服务端发送消息
上面的代码只是保持了长连接,而且是单向的,只能是服务端给客户端发消息。
单向的意思就是,客户端不能通过sse去发送消息,服务端可以通过sse给客户端发送消息。
但是我们还是可以使用sse来完成聊天功能的,因为客户端可以通过普通http请求去发送消息,到服务端以后再发送给其他客户端。
示例:
@requestmapping(value = "/send")
public resultmodel send(@requestbody messagedto<string> messagedto, httpservletrequest request) {
logger.info("收到发往用户[{}]的文本请求;", messagedto.gettargetusername());
object username = request.getsession().getattribute("username");
if (username == null)
return resultmodel.error("无用户");
messagedto.setfromusername((string) username);
messagedto.setmessagetype(type.type_text.getmessagetype());
chater chater = websseuser.getchater(messagedto.gettargetusername());
try {
chater.getsseemitter().send(messagedto);
} catch (ioexception e) {
e.printstacktrace();
}
return resultmodel.ok();
}这里,通过目标的username获取到chater对象,然后chater对象中保存有sseemitter对象,sseemitter对象可以直接发送消息到客户端。
三、客户端
前面讲述了服务端维持sse的方法。下面讲述下客户端如何操作。
3.1 sse连接
调用服务端的/subscribe接口,维持长连接,请阅服务端消息。
var url = "/subscribe";
var es = new eventsource(url);
es.addeventlistener("message", function(e){
decode(e);
},false);
不需要额外引入js,addeventlistener中可以调用其他方法对消息解析并操作。
这里的decode(e);是对应服务端的chater.getsseemitter().send(messagedto);
3.2 普通http请求发送消息
普通的ajax请求即可,无需额外处理,调用服务端的/send接口即可。
四、控制台截图
如图:
- 客户端连接服务端的
/subscribe接口,这个连接会一直持续下去,图上已经持续了十几秒。 - 客户端连接服务端的
/send接口,发送消息给服务端,服务端转发给其他客户端。
到此这篇关于spring boot中使用sse(server-sent events)实现聊天功能:替代websocket服务器推送的文章就介绍到这了,更多相关sse(server-sent events)替代websocket内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论