前言

web开发也讲解了三章了,这章节开始讲解关于与前端通信相关知识。实现一个在线聊天室类似的功能或者后端推送消息到前端,在没有WebSocket时,读大学那伙还有接触过DWR(Direct Web Remoting),也使用过轮询的方式,当Servlet3.0出来后,也有使用其异步连接机制进行前后端通信的。今天我们就来说说WebSocket。它是HTML5开始提供的。

关于WebSocket

WebSocketHTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议。

WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

浏览器通过JavaScript向服务器发出建立WebSocket连接的请求,连接建立以后,客户端和服务器端就可以通过TCP连接直接交换数据。

当获取Web Socket连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。

对于前端,创建一个WebSocket对象,如下:

var Socket = new WebSocket(url, [protocol] );

说明:第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。

WebSocker属性

以下是WebSocket对象的属性。假定我们使用了以上代码创建了Socket对象:

属性 描述
Socket.readyState 只读属性 readyState 表示连接状态,可以是以下值:
0 - 表示连接尚未建立。
1 - 表示连接已建立,可以进行通信。
2 - 表示连接正在进行关闭。
3 - 表示连接已经关闭或者连接不能打开。
Socket.bufferedAmount 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。

WebSocket事件

以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:

事件 事件处理程序 描述
open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发

WebSocket方法

以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:

方法 描述
Socket.send() 使用连接发送数据
Socket.close() 关闭连接

WebSocket实践

前面介绍了在浏览器端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));
}
}

注意点:

  1. @ServerEndpoint的value值填写时,开头需要加上/,不然会提示路径无效。
  2. 需要加上类型@Component注解,使得能被扫描到。
  3. 这里的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,连接后就可以看见服务器返回的消息了。

我们再开一个标签页,然后继续以另一个身份进入:

这时,可以看见第一个页面开的,也收到消息了。现在我们发送一条消息:

然后,其中一个断开连接:

然后可以愉快聊天了,简单的一个聊天室就完成了。

参考资料

  1. https://docs.spring.io/spring/docs/4.3.18.RELEASE/spring-framework-reference/htmlsingle/#websocket
  2. https://docs.spring.io/spring-boot/docs/1.5.15.RELEASE/reference/htmlsingle/#boot-features-websockets
  3. http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html
  4. http://www.runoob.com/html/html5-websocket.html

总结

本章节主要是讲解了WebSocket的使用。因为有统一标准的存在,编写webSocket也是很简单的。对于如何一对一聊天,大家可以自行编写下,因为知道了对方名称,就能找出对方的session然后就能发送消息了。

最后

目前互联网上很多大佬都有SpringBoot系列教程,如有雷同,请多多包涵了。本文是作者在电脑前一字一句敲的,每一步都是自己实践的。若文中有所错误之处,还望提出,谢谢。

老生常谈

  • 个人QQ: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/

SpringBoot | 第十九章:web应用开发之WebSocket的更多相关文章

  1. SpringBoot | 第十八章:web应用开发之WebJars使用

    前言 前面一章节我们主要讲解了关于文件上传的两种方式.本章节继续web开发的相关知识点.通常对于web开发而言,像js.css.images等静态资源版本管理是比较混乱的,比如Jquery.Boots ...

  2. SpringBoot | 第二十九章:Dubbo的集成和使用

    前言 今年年初时,阿里巴巴开源的高性能服务框架dubbo又开始了新一轮的更新,还加入了Apache孵化器.原先项目使用了spring cloud之后,已经比较少用dubbo.目前又抽调回原来的行业应用 ...

  3. 第十九章——使用资源调控器管理资源(1)——使用SQLServer Management Studio 配置资源调控器

    原文:第十九章--使用资源调控器管理资源(1)--使用SQLServer Management Studio 配置资源调控器 本系列包含: 1. 使用SQLServer Management Stud ...

  4. Gradle 1.12翻译——第十九章. Gradle 守护进程

    有关其他已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或访问:http://gradledoc.qiniudn.com ...

  5. 20190908 On Java8 第十九章 类型信息

    第十九章 类型信息 RTTI(RunTime Type Information,运行时类型信息)能够在程序运行时发现和使用类型信息. Java 主要有两种方式在运行时识别对象和类信息: "传 ...

  6. Python之路【第十九章】:Django进阶

    Django路由规则 1.基于正则的URL 在templates目录下创建index.html.detail.html文件 <!DOCTYPE html> <html lang=&q ...

  7. 第十九章——使用资源调控器管理资源(2)——使用T-SQL配置资源调控器

    原文:第十九章--使用资源调控器管理资源(2)--使用T-SQL配置资源调控器 前言: 在前一章已经演示了如何使用SSMS来配置资源调控器.但是作为DBA,总有需要写脚本的时候,因为它可以重用及扩展. ...

  8. 第十九章 Django的ORM映射机制

    第十九章 Django的ORM映射机制 第一课 Django获取多个数据以及文件上传 1.获取多选的结果(checkbox,select/option)时: req.POST.getlist('fav ...

  9. Gradle 1.12用户指南翻译——第四十九章. Build Dashboard 插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

随机推荐

  1. SQL Server BCP 资料导入导出

    SQL Server BCP 导入导出使用 Bcp 导出导入数据高效,比使用SQL Server Management Stdio 提供的数据库导出导入要高效因为sql server 也没有提供提供类 ...

  2. VisualGDB系列9:配置VS直接通过SSH方式访问Linux项目

    根据VisualGDB官网(https://visualgdb.com)的帮助文档大致翻译而成.主要是作为个人学习记录.有错误的地方,Robin欢迎大家指正. 本文介绍如何使用VS和VisualGDB ...

  3. C++ 替换字符串内某个字符或子串

    1. 问题描述 string s="abc"; string tmp="1"; 2.解决方案 // tmp 必须为字符串 // 第一个 1 表示 s 中的位置 ...

  4. Jenkins搭建Nodejs自动化测试

    一.安装Jenkins(Windows) 1. 在Jenkins官网(https://jenkins.io/)下载安装包,解压并安装 2. 安装完成后,会自动打开一个页面,根据提示在安装目录下找到随机 ...

  5. C#的闭包

    简单的理解:闭包变量是把局部变量的作用域扩展到回调函数,发生在匿名方法注册到委托上,而匿名方法中使用外部的局部变量 说什么都不如图示那么容易明白啊 先看C#的源码 class Program { st ...

  6. [java] volatile关键字对while循环条件提升问题补充

    在java并发编程中,代码如下: volatile boolean asleep; ... while(!asleep){ countSomeSheep(); } 如果此处忘记将asleep变量设置为 ...

  7. ps查看进程

    ps:要对进程进行监测和控制,首先必须要了解当前进程的情况,也就是需要查看当前进程,而ps命令就是最基本同时也是非常强大的进程查看命令.使用该命令可以确定有哪些进程正在运行和运行的状态.进程是否结束. ...

  8. .net开发ActiveX控件

    我估计有些朋友不清楚ActiveX控件,但这篇博客不是来解释这些概念的.如果你对ActiveX的概念不清楚,请参考这里: http://baike.baidu.com/view/28141.htm 这 ...

  9. 12、多空格&多制表符文本之cut域分割终极方案

    解决方法分为如下三步: (1)        使用“tr”命令将制表符转换为空格: (2)        使用“tr”命令将多个重复空格删除,保留一个空格: (3)        使用“cut”命令进 ...

  10. Luogu 4868 Preprefix sum

    类似于树状数组维护区间的方法. 每一次询问要求$\sum_{i = 1}^{n}\sum_{j = 1}^{i}a_j$. 展开一下: $\sum_{i = 1}^{n}\sum_{j = 1}^{i ...