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实现 LeetCode 677 键值映射(字典树)
677. 键值映射 实现一个 MapSum 类里的两个方法,insert 和 sum. 对于方法 insert,你将得到一对(字符串,整数)的键值对.字符串表示键,整数表示值.如果键已经存在,那么原来 ...
- Java实现 LeetCode 343 整数拆分(动态规划入门经典)
343. 整数拆分 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化. 返回你可以获得的最大乘积. 示例 1: 输入: 2 输出: 1 解释: 2 = 1 + 1, 1 × ...
- Java实现蓝桥杯VIP算法训练 相邻字母
试题 算法训练 相邻字母 资源限制 时间限制:1.0s 内存限制:256.0MB [问题描述] 从键盘输入一个英文字母,要求按字母的顺序打印出3个相邻的字母,指定的字母在中间.若指定的字母为Z,则打印 ...
- Java实现Fibonacci取余
Description Fibonacci数列的递推公式为:Fn=Fn-1+Fn-2,其中F1=F2=1. 当n比较大时,Fn也非常大,现在我们想知道,Fn除以10007的余数是多少. Input 多 ...
- Java中构造方法的详细介绍
构造方法是一个特殊的方法 它会在实例化对象时自动调用 构造方法的定义 必须同时满足下面的三个条件 方法名与类名相同 方法名前面没有返回值类型的声明 在方法中不能使用return语句返回值 class ...
- CSDN账号被冻结了怎么办
CSDN可能因为你的博客里有一些网站链接给你判断为恶意推广广告,冻结, 或者和我一样,在评论区刷屏被冻结, 联系客服即可,向客服提供你的绑定邮箱或绑定手机号,或博客id,客服会给你解冻 PS: 找不到 ...
- java实现机器人行走
某少年宫引进了一批机器人小车.可以接受预先输入的指令,按指令行动.小车的基本动作很简单,只有3种:左转(记为L),右转(记为R),向前走若干厘米(直接记数字). 例如,我们可以对小车输入如下的指令: ...
- 团体天梯赛L1-041.寻找250
对方不想和你说话,并向你扔了一串数…… 而你必须从这一串数字中找到“250”这个高大上的感人数字. 输入格式: 输入在一行中给出不知道多少个绝对值不超过1000的整数,其中保证至少存在一个“250”. ...
- cuda基础
一:cuda编程模型 1:主机与设备 主机---CPU 设备/处理器---GPU CUDA编程模型如下: GPU多层存储空间结构如图: 2:Kernel函数的定义与调用 A:运行在GPU上,必须通过_ ...
- Java线程变量问题-ThreadLocal
关于Java线程问题,在博客上看到一篇文章挺好的: https://blog.csdn.net/w172087242/article/details/83375022#23_ThreadLocal_1 ...