SpringBoot 是为了简化 Spring 应用的创建、运行、调试、部署等一系列问题而诞生的产物,自动装配的特性让我们可以更好的关注业务本身而不是外部的XML配置,我们只需遵循规范,引入相关的依赖就可以轻易的搭建出一个 WEB 工程
Webscoket 对浏览器有一定的要求,所以使用之前要考虑兼容性的问题….
Webscoket
WebSocket 是 HTML5 新增的一种在单个 TCP 连接上进行全双工通讯的协议,与 HTTP 协议没有太大关系….
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 WebSocket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage() 事件来接收服务器返回的数据..
长连接
与 AJAX 轮训的方式差不多,但长连接不像 AJAX 轮训一样,而是采用的阻塞模型(一直打电话,没收到就不挂电话);客户端发起连接后,如果没消息,就一直不返回 Response 给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
在没有 WebSocket 之前,大家常用的手段应该就是轮训了,比如每隔几秒发起一次请求,但这样带来的就是高性能开销,都知道一次 HTTP 响应是需要经过三次握手和四次挥手,远不如 TCP 长连接来的划算
WebSocket 事件
WebSocket 事件
本章目标
利用 Spring Boot 与 WebSocke 打造 一对一 和 一对多 的在线聊天….
导入依赖
依赖 spring-boot-starter-websocket…
1 2 3 4 5 6 7 8 9 10
|
<dependencies> <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> </dependencies>
|
属性配置
工具类
为了减少代码量,此处就不集成 Redis、Mysql 之类的存储化依赖…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
package com.battcn.utils;
import javax.websocket.RemoteEndpoint; import javax.websocket.Session; import java.io.IOException; import java.util.Map; import java.util.concurrent.ConcurrentHashMap;
/** * @author Levin * @since 2018/6/26 0026 */ public final class WebSocketUtils {
/** * 模拟存储 websocket session 使用 */ public static final Map<String, Session> LIVING_SESSIONS_CACHE = new ConcurrentHashMap<>();
public static void sendMessageAll(String message) { LIVING_SESSIONS_CACHE.forEach((sessionId, session) -> sendMessage(session, message)); }
/** * 发送给指定用户消息 * * @param session 用户 session * @param message 发送内容 */ public static void sendMessage(Session session, String message) { if (session == null) { return; } final RemoteEndpoint.Basic basic = session.getBasicRemote(); if (basic == null) { return; } try { basic.sendText(message); } catch (IOException e) { e.printStackTrace(); } } }
|
服务端点
@ServerEndpoint 中的内容就是 WebSocket 协议的地址,其实仔细看会发现与 @RequestMapping 也是异曲同工的…
- HTTP 协议:
http://localhost:8080/path
- WebSocket 协议:
ws://localhost:8080/path
@OnOpen、@OnMessage、@OnClose、@OnError 注解与 WebSocket 中监听事件是相对应的…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
|
package com.battcn.websocket;
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;
import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException;
import static com.battcn.utils.WebSocketUtils.LIVING_SESSIONS_CACHE; import static com.battcn.utils.WebSocketUtils.sendMessage; import static com.battcn.utils.WebSocketUtils.sendMessageAll;
/** * 聊天室 * * @author Levin * @since 2018/6/26 0026 */ @RestController @ServerEndpoint("/chat-room/{username}") public class ChatRoomServerEndpoint {
private static final Logger log = LoggerFactory.getLogger(ChatRoomServerEndpoint.class);
@OnOpen public void openSession(@PathParam("username") String username, Session session) { LIVING_SESSIONS_CACHE.put(username, session); String message = "欢迎用户[" + username + "] 来到聊天室!"; log.info(message); sendMessageAll(message);
}
@OnMessage public void onMessage(@PathParam("username") String username, String message) { log.info(message); sendMessageAll("用户[" + username + "] : " + message); }
@OnClose public void onClose(@PathParam("username") String username, Session session) { //当前的Session 移除 LIVING_SESSIONS_CACHE.remove(username); //并且通知其他人当前用户已经离开聊天室了 sendMessageAll("用户[" + username + "] 已经离开聊天室了!"); try { session.close(); } catch (IOException e) { e.printStackTrace(); } }
@OnError public void onError(Session session, Throwable throwable) { try { session.close(); } catch (IOException e) { e.printStackTrace(); } throwable.printStackTrace(); }
@GetMapping("/chat-room/{sender}/to/{receive}") public void onMessage(@PathVariable("sender") String sender, @PathVariable("receive") String receive, String message) { sendMessage(LIVING_SESSIONS_CACHE.get(receive), "[" + sender + "]" + "-> [" + receive + "] : " + message); }
}
|
聊天室 HTML
onopen 建立 WebSocket 连接时触发。
message 客户端监听服务端事件,当服务端向客户端推送消息时会被监听到。
error WebSocket 发生错误时触发。
close 关闭 WebSocket 连接时触发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
|
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>battcn websocket</title> <script src="jquery-3.2.1.min.js" ></script> </head> <body>
<label for="message_content">聊 天 室 </label><textarea id="message_content" readonly="readonly" cols="57" rows="10">
</textarea>
<br/>
<label for="in_user_name">用户姓名 </label><input id="in_user_name" value=""/> <button id="btn_join">加入聊天室</button> <button id="btn_exit">离开聊天室</button>
<br/><br/>
<label for="in_room_msg">群发消息 </label><input id="in_room_msg" value=""/> <button id="btn_send_all">发送消息</button>
<br/><br/><br/>
好友聊天 <br/> <label for="in_sender">发送者 </label><input id="in_sender" value=""/><br/> <label for="in_receive">接受者 </label><input id="in_receive" value=""/><br/> <label for="in_point_message">消息体 </label><input id="in_point_message" value=""/><button id="btn_send_point">发送消息</button>
</body>
<script type="text/javascript"> $(document).ready(function(){ var urlPrefix ='ws://localhost:8080/chat-room/'; var ws = null; $('#btn_join').click(function(){ var username = $('#in_user_name').val(); var url = urlPrefix + username; ws = new WebSocket(url); ws.onopen = function () { console.log("建立 websocket 连接..."); }; ws.onmessage = function(event){ //服务端发送的消息 $('#message_content').append(event.data+'\n'); }; ws.onclose = function(){ $('#message_content').append('用户['+username+'] 已经离开聊天室!'); console.log("关闭 websocket 连接..."); } }); //客户端发送消息到服务器 $('#btn_send_all').click(function(){ var msg = $('#in_room_msg').val(); if(ws){ ws.send(msg); } }); // 退出聊天室 $('#btn_exit').click(function(){ if(ws){ ws.close(); } });
$("#btn_send_point").click(function() { var sender = $("#in_sender").val(); var receive = $("#in_receive").val(); var message = $("#in_point_message").val(); $.get("/chat-room/"+sender+"/to/"+receive+"?message="+message,function() { alert("发送成功...") }) })
}) </script>
</html>
|
主函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
package com.battcn;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/** * @author Levin */ @EnableWebSocket @SpringBootApplication public class Chapter24Application {
public static void main(String[] args) { SpringApplication.run(Chapter24Application.class, args);
}
@Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
|
测试
启动 Chapter24Application.java 中的 main 方法,为了更好的演示效果这里打开了俩浏览器窗口做的测试…
测试结果
- JAVA-Socket通信 打造属于自己的聊天室(服务端)
我们每天都在使用着微信.QQ等聊天软件,但不知你是否有想过这些聊天软件是如何实现的?是否想过要制作一个属于自己的聊天室? 本篇博客将带你打造一个简单的属于自己的聊天室,将cmd作为聊天窗口,可通过内网 ...
- 用NodeJS打造多人在线聊天室(NodeJS & SocketIO & Express & EJS & MongoDB & Gulp)
项目背景 这个项目主要是为了玩玩NodeJS,项目的方向大概是做出类似QQ的在线聊天系统.想要在线体验可以点击在线演示. 项目使用PM2进行部署和管理,功能在不断的迭代开发中.如果你觉得这个项目比较有 ...
- WebSocket聊天室demo
根据Socket异步聊天室修改成WebSocket聊天室 WebSocket特别的地方是 握手和消息内容的编码.解码(添加了ServerHelper协助处理) ServerHelper: using ...
- 分享基于 websocket 网页端聊天室
博客地址:https://ainyi.com/67 有一个月没有写博客了,也是因为年前需求多.回家过春节的原因,现在返回北京的第二天,想想,应该也要分享技术专题的博客了!! 主题 基于 websock ...
- 基于 OpenResty 实现一个 WS 聊天室
基于 OpenResty 实现一个 WS 聊天室 WebSocket WebSocket 协议分析 WebSocket 协议解决了浏览器和服务器之间的全双工通信问题.在WebSocket出现之前,浏览 ...
- FastAPI(56)- 使用 Websocket 打造一个迷你聊天室
背景 在实际项目中,可能会通过前端框架使用 WebSocket 和后端进行通信 这里就来详细讲解下 FastAPI 是如何操作 WebSocket 的 模拟 WebSocket 客户端 #!usr/b ...
- 使用socket.io打造公共聊天室
最近的计算机网络课上老师开始讲socket,tcp相关的知识,当时脑袋里就蹦出一个想法,那就是打造一个聊天室.实现方式也挺多的,常见的可以用C++或者Java进行socket编程来构建这么一个聊天室. ...
- nodejs+mongoose+websocket搭建xxx聊天室
简介 本文是由nodejs+mongoose+websocket打造的一个即时聊天系统:本来打算开发一个类似于网页QQ类似功能的聊天系统,但是目前只是开发了一个模块功能 --- 类似群聊的,即一对多的 ...
- 利用socket.io+nodejs打造简单聊天室
代码地址如下:http://www.demodashi.com/demo/11579.html 界面展示: 首先展示demo的结果界面,只是简单消息的发送和接收,包括发送文字和发送图片. ws说明: ...
随机推荐
- Java实现蓝桥杯历届试题回文数字
历届试题 回文数字 时间限制:1.0s 内存限制:256.0MB 提交此题 问题描述 观察数字:12321,123321 都有一个共同的特征,无论从左到右读还是从右向左读,都是相同的.这样的数字叫做: ...
- Java实现 蓝桥杯VIP 算法训练 快速排序
import java.util.Scanner; public class 快速排序 { public static void main(String[] args){ Scanner sc=new ...
- Java实现 LeetCode 239 滑动窗口最大值
239. 滑动窗口最大值 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧.你只可以看到在滑动窗口内的 k 个数字.滑动窗口每次只向右移动一位. 返回滑动窗口中的最 ...
- java实现第六届蓝桥杯居民集会
居民集会 蓝桥村的居民都生活在一条公路的边上,公路的长度为L,每户家庭的位置都用这户家庭到公路的起点的距离来计算,第i户家庭距起点的距离为di. 每年,蓝桥村都要举行一次集会.今年,由于村里的人口太多 ...
- js数组的常见操作( push、pop、unshift、shift、splice、concat、 join)的用法
1.数组添加删除 头部或尾部( push().pop().unshift().shift() ) 例2.数组尾部添加 push()方法可向数组的末尾添加一个或多个元素,并返回新的长度 语法:array ...
- windows10+office2019各版本激活密钥
[windows10激活密钥] 1.win10专业版 236TW-X778T-8MV9F-937GT-QVKBB 87VT2-FY2XW-F7K39-W3T8R-XMFGF KH2J9-PC326-T ...
- uni-app热更新
开发工具HbuilderX开发框架 uni-app.h5+1.生成 App 资源升级包1.1.修改版本号1.2.首先,更新 manifest.json 中的版本号.比如之前是 1.0.0,那么新版本应 ...
- C# 9.0 新特性之模式匹配简化
阅读本文大概需要 2 分钟. 记得在 MS Build 2020 大会上,C# 语言开发项目经理 Mads Torgersen 宣称 C# 9.0 将会随着 .NET 5 在今年 11 月份正式发布. ...
- 前后端分离项目 nginx配置实践
新项目采用前后端分离的方式开发,前后端代码打算分开部署(同机器且同域名),但打算支持后端依然可访问静态资源. 搜索nginx配置大部分都通过url前缀进行转发来做前后端分离,不适用目前项目. 说明 前 ...
- ubuntu 显示桌面快捷键
ubuntu 显示桌面快捷键 快速显示桌面的快捷键是 ctrl + win + d win:就是窗口键,在键盘左侧ctrl与Alt之间的那个建.