WebSocket的实现与应用

WebSocket的实现与应用 前言

说到websocket,就不得不提http协议的连接特点特点与交互模型。

首先,http协议的特点是无状态连接。即http的前一次连接与后一次连接是相互独立的。

其次,http的交互模型是请求/应答模型。即交互是通过C/B端向S端发送一个请求,S端根据请求,返回一个响应。

那么这里就有一个问题了--S端无法主动向C/B端发送消息。而交互是双方的事情,怎么能限定一方发数据,另一方接数据呢。

传统解决方案:

传统的解决方案就俩字:轮询。

长短连接轮询就不详细说了,就说说轮询。大概的场景是这样的:

客户端(Request):有消息不?

服务端(Response):No

客户端(Request):有消息不?

服务端(Response):No

客户端(Request):有消息不?

服务端(Response):No

客户端(Request):有消息不?

服务端(Response):有了。你妈叫你回家吃饭。

客户端(Request):有消息不?

服务端(Response):No

==================================> loop

看着都累,资源消耗那就更不必说了。尤其有些对实时性要求高的数据,那可能就是1s请求一次。目测服务器已经泪奔。

websocket解决方案:

那么websocket的解决方案,总结一下,就是:建立固定连接

说白了,就是C/B端与S端就一个websocket服务建立一个固定的连接,不断开。

大概的场景是这样的:

服务端:我建立了一个chat的websocket,欢迎大家连接。

客户端:我要和你的chat的websocket连接,我的sid(唯一标识)是No.1

服务端:好的,我已经记住你了。如果有发往chat下No.1的消息,我会告诉你的。

客户端:嗯。谢谢了哈。

==================================> 过了一段时间

(有一个请求调用了chat的websocket,并且指名是给No.1的消息)

服务端(发送消息给No.1):No.1,有你的消息。你妈妈叫你回家做作业。

客户端(No.1):好的。我收到了。谢谢。

由于这次只是简单说一下websocket,所以就不深入解读网络相关知识了。

应用场景

既然http无法满足用户的所有需求,那么为之诞生的websocket必然有其诸多应用场景。如:

实时显示网站在线人数

账户余额等数据的实时更新

多玩家网络游戏

多媒体聊天,如聊天室

。。。

其实总结一下,websocket的应用场景就俩字:实时

无论是多玩家网络游戏,网站在线人数等都是由于实时性的需求,才用上了websocket(后面用缩写ws)。

谈几个在我项目中用到的情景:

在线教育项目中的课件系统,通过ws实现学生端课件与教师端课件的实时交互

物联网项目中的报警系统,通过ws实现报警信息的实时推送

大数据项目中的数据展示,通过ws实现数据的实时更新

物联网项目中的硬件交互系统,通过ws实现硬件异步响应的展示

当你的项目中存在需要S端向C/B端发送数据的情形,那就可以考虑上一个websocket了。

实现 服务端开发: 引入依赖: <!-- websocket --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> 添加配置:

忍不住想要吐槽,为什么不可以如eureka等组件那样,直接在启动类写一个注解就Ok了呢。看来还得以后自己动手,丰衣足食啊。

package com.renewable.center.warning.configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; /** * Websocket的配置 * 说白了就是引入Websocekt至spring容器 */ @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } 代码实现: WebSocketServer的实现: package com.renewable.center.warning.controller.websocket; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; /** * @Description: * @Author: jarry */ @Component @Slf4j @ServerEndpoint("/websocket/warning/{sid}") public class WarningWebSocketServer { // JUC包的线程安全Set,用来存放每个客户端对应的WarningWebSocketServer对象。 // 用ConcurrentHashMap也是可以的。说白了就是类似线程池中的BlockingQueue那样作为一个容器 private static CopyOnWriteArraySet<WarningWebSocketServer> warningWebSocketSet = new CopyOnWriteArraySet<WarningWebSocketServer>(); // 与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session; // 接收sid private String sid=""; /** * 建立websocket连接 * 看起来很像JSONP的回调,因为前端那里是Socket.onOpne() * @param session * @param sid */ @OnOpen public void onOpen(Session session, @PathParam("sid") String sid){ this.session = session; this.sid = sid; warningWebSocketSet.add(this); sendMessage("websocket connection has created."); } /** * 关闭websocket连接 */ @OnClose public void onClose(){ warningWebSocketSet.remove(this); log.info("there is an wsConnect has close ."); } /** * websocket连接出现问题时的处理 */ @OnError public void onError(Session session, Throwable error){ log.error("there is an error has happen ! error:{}",error); } /** * websocket的server端用于接收消息的(目测是用于接收前端通过Socket.onMessage发送的消息) * @param message */ @OnMessage public void onMessage(String message){ log.info("webSocketServer has received a message:{} from {}", message, this.sid); // 调用消息处理方法(此时针对的WarningWebSocektServer对象,只是一个实例。这里进行消息的单发) // 目前这里还没有处理逻辑。故为了便于前端调试,这里直接返回消息 this.sendMessage(message); } /** * 服务器主动推送消息的方法 */ public void sendMessage(String message){ try { this.session.getBasicRemote().sendText(message); } catch (IOException e) { log.warn("there is an IOException:{}!",e.toString()); } } public static void sendInfo(String sid, String message){ for (WarningWebSocketServer warningWebSocketServerItem : warningWebSocketSet) { if (StringUtils.isBlank(sid)){ // 如果sid为空,即群发消息 warningWebSocketServerItem.sendMessage(message); log.info("Mass messaging. the message({}) has sended to sid:{}.", message,warningWebSocketServerItem.sid); } if (StringUtils.isNotBlank(sid)){ if (warningWebSocketServerItem.sid.equals(sid)){ warningWebSocketServerItem.sendMessage(message); log.info("single messaging. message({}) has sended to sid:{}.", message, warningWebSocketServerItem.sid); } } } } } WesocketController

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zysgzw.html