web开发
也讲解了三章了,这章节开始讲解关于与前端通讯相关知识。实现一个在线聊天室相似的功能或者后端推送消息到前端,在没有WebSocket
时,读大学那伙还有接触过DWR(Direct Web Remoting)
,也使用过轮询的方式,当Servlet3.0
出来后,也有使用其异步链接机制进行先后端通讯的。今天咱们就来讲说WebSocket
。它是HTML5
开始提供的。html
WebSocket
是HTML5
开始提供的一种在单个TCP
链接上进行全双工
通信的协议。前端
在WebSocket API
中,浏览器和服务器只须要作一个握手的动做,而后,浏览器和服务器之间就造成了一条快速通道。二者之间就直接能够数据互相传送。html5
浏览器经过JavaScript
向服务器发出创建WebSocket
链接的请求,链接创建之后,客户端和服务器端就能够经过TCP
链接直接交换数据。java
当获取Web Socket
链接后,你能够经过 send() 方法来向服务器发送数据,并经过 onmessage 事件来接收服务器返回的数据。git
对于前端,建立一个WebSocket
对象,以下:github
var Socket = new WebSocket(url, [protocol] );
说明:第一个参数 url, 指定链接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。web
如下是WebSocket
对象的属性。假定咱们使用了以上代码建立了Socket
对象:spring
属性 | 描述 |
---|---|
Socket.readyState | 只读属性 readyState 表示链接状态,能够是如下值:<br>0 - 表示链接还没有创建。<br> 1 - 表示链接已创建,能够进行通讯。<br>2 - 表示链接正在进行关闭。<br>3 - 表示链接已经关闭或者链接不能打开。 |
Socket.bufferedAmount | 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,可是尚未发出的 UTF-8 文本字节数。 |
如下是 WebSocket 对象的相关事件。假定咱们使用了以上代码建立了 Socket 对象:后端
事件 | 事件处理程序 | 描述 |
---|---|---|
open | Socket.onopen | 链接创建时触发 |
message | Socket.onmessage | 客户端接收服务端数据时触发 |
error | Socket.onerror | 通讯发生错误时触发 |
close | Socket.onclose | 链接关闭时触发 |
如下是 WebSocket 对象的相关方法。假定咱们使用了以上代码建立了 Socket 对象:浏览器
方法 | 描述 |
---|---|
Socket.send() | 使用链接发送数据 |
Socket.close() | 关闭链接 |
前面介绍了在
浏览器端
中webSocket
的相关知识点,如今咱们就来搭建一个后台对接应用,以实现一个简单的在线聊天室。
后端关于
WebSocket
的实现是基于JSR356
标准的。该标准的出现,统一了WebSocket
的代码写法。只要支持web容器支持JSR356
标准,那么实现方式是一致的。而目前实现方式有两种,一种是注解
方式,另外一种就是继承继承javax.websocket.Endpoint
类了。
@WebSocketEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端。注解的值将被用于监听用户链接的终端访问URL地址。
@onOpen 打开一个新链接,即有新链接时,会调用被此注解的方法。
@onClose 关闭链接时调用。
@onMessage 当服务器接收到客户端发送的消息时所调用的方法。
@PathParam 接收uri
参数的,与@PathVariable功能差很少,可经过url获取对应值
0.加入POM
依赖。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
1.编写控制层,对应WebSocket
的各事件。同时抽取了个公用类,进行通用方法调用。
WebSocketController.java
/** * websocket 简易聊天 * @author oKong * */ //因为是websocket 因此本来是@RestController的http形式 //直接替换成@ServerEndpoint便可,做用是同样的 就是指定一个地址 //表示定义一个websocket的Server端 @Component @ServerEndpoint(value = "/my-chat/{usernick}") @Slf4j public class WebSocketController { /** * 链接事件 加入注解 * @param session */ @OnOpen public void onOpen(@PathParam(value = "usernick") String userNick,Session session) { String message = "有新游客[" + userNick + "]加入聊天室!"; log.info(message); WebSocketUtil.addSession(userNick, session); //此时可向全部的在线通知 某某某登陆了聊天室 WebSocketUtil.sendMessageForAll(message); } @OnClose public void onClose(@PathParam(value = "usernick") String userNick,Session session) { String message = "游客[" + userNick + "]退出聊天室!"; log.info(message); WebSocketUtil.remoteSession(userNick); //此时可向全部的在线通知 某某某登陆了聊天室 WebSocketUtil.sendMessageForAll(message); } @OnMessage public void OnMessage(@PathParam(value = "usernick") String userNick, String message) { //相似群发 String info = "游客[" + userNick + "]:" + message; log.info(info); WebSocketUtil.sendMessageForAll(message); } @OnError public void onError(Session session, Throwable throwable) { log.error("异常:", throwable); try { session.close(); } catch (IOException e) { e.printStackTrace(); } throwable.printStackTrace(); } }
WebSocketUtil.java
public class WebSocketUtil { /** * 简单使用map进行存储在线的session * */ private static final Map<String, Session> ONLINE_SESSION = new ConcurrentHashMap<>(); public static void addSession(String userNick,Session session) { //putIfAbsent 添加键—值对的时候,先判断该键值对是否已经存在 //不存在:新增,并返回null //存在:不覆盖,直接返回已存在的值 // ONLINE_SESSION.putIfAbsent(userNick, session); //简单示例 不考虑复杂状况。。怎么简单怎么来了。。 ONLINE_SESSION.put(userNick, session); } public static void remoteSession(String userNick) { ONLINE_SESSION.remove(userNick); } /** * 向某个用户发送消息 * @param session 某一用户的session对象 * @param message */ public static void sendMessage(Session session, String message) { if(session == null) { return; } // getAsyncRemote()和getBasicRemote()异步与同步 Async async = session.getAsyncRemote(); //发送消息 async.sendText(message); } /** * 向全部在线人发送消息 * @param message */ public static void sendMessageForAll(String message) { //jdk8 新方法 ONLINE_SESSION.forEach((sessionId, session) -> sendMessage(session, message)); } }
注意点:
/
,否则会提示路径无效。@Component
注解,使得能被扫描到。session
等,都在包**javax.websocket
**包下的,注意区分。2.编写主启动类,主要是加入注解@EnableWebSocket
和申明一个Websocket endpoint
类。
@SpringBootApplication @EnableWebSocket @Slf4j public class Chapter19Application { public static void main(String[] args) { SpringApplication.run(Chapter19Application.class, args); log.info("Chapter19启动!"); } /** * 会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint * 要注意,若是使用独立的servlet容器, * 而不是直接使用springboot的内置容器, * 就不要注入ServerEndpointExporter,由于它将由容器本身提供和管理。 */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
3.启动应用,利用在线的测试工具进行测试。这里直接使用了http://coolaf.com/tool/chattest进行测试。固然也能够本身写一个html
了。
首先,输入咱们的服务地址:ws://127.0.0.1:8080/my-chat/okong,链接后就能够看见服务器返回的消息了。
咱们再开一个标签页,而后继续以另外一个身份进入:
这时,能够看见第一个页面开的,也收到消息了。如今咱们发送一条消息:
而后,其中一个断开链接:
而后能够愉快聊天了,简单的一个聊天室就完成了。
本章节主要是讲解了
WebSocket
的使用。由于有统一标准的存在,编写webSocket
也是很简单的。对于如何一对一聊天,你们能够自行编写下,由于知道了对方名称,就能找出对方的session
而后就能发送消息了。
目前互联网上不少大佬都有
SpringBoot
系列教程,若有雷同,请多多包涵了。本文是做者在电脑前一字一句敲的,每一步都是本身实践的。若文中有所错误之处,还望提出,谢谢。
499452441
lqdevOps
我的博客:http://blog.lqdev.cn
完整示例:https://github.com/xie19900123/spring-boot-learning/tree/master/chapter-19
原文地址:http://blog.lqdev.cn/2018/08/14/springboot/chapter-nineteen/