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>

属性配置

工具类

为了减少代码量,此处就不集成 RedisMysql 之类的存储化依赖…

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">聊&nbsp;&nbsp;天&nbsp;&nbsp;室&nbsp;</label><textarea id="message_content" readonly="readonly" cols="57" rows="10"> </textarea> <br/> <label for="in_user_name">用户姓名 &nbsp;</label><input id="in_user_name" value=""/>
<button id="btn_join">加入聊天室</button>
<button id="btn_exit">离开聊天室</button> <br/><br/> <label for="in_room_msg">群发消息 &nbsp;</label><input id="in_room_msg" value=""/>
<button id="btn_send_all">发送消息</button> <br/><br/><br/> 好友聊天
<br/>
<label for="in_sender">发送者 &nbsp;</label><input id="in_sender" value=""/><br/>
<label for="in_receive">接受者 &nbsp;</label><input id="in_receive" value=""/><br/>
<label for="in_point_message">消息体 &nbsp;</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 方法,为了更好的演示效果这里打开了俩浏览器窗口做的测试…

测试结果

打造属于你的聊天室(WebSocket)的更多相关文章

  1. JAVA-Socket通信 打造属于自己的聊天室(服务端)

    我们每天都在使用着微信.QQ等聊天软件,但不知你是否有想过这些聊天软件是如何实现的?是否想过要制作一个属于自己的聊天室? 本篇博客将带你打造一个简单的属于自己的聊天室,将cmd作为聊天窗口,可通过内网 ...

  2. 用NodeJS打造多人在线聊天室(NodeJS & SocketIO & Express & EJS & MongoDB & Gulp)

    项目背景 这个项目主要是为了玩玩NodeJS,项目的方向大概是做出类似QQ的在线聊天系统.想要在线体验可以点击在线演示. 项目使用PM2进行部署和管理,功能在不断的迭代开发中.如果你觉得这个项目比较有 ...

  3. WebSocket聊天室demo

    根据Socket异步聊天室修改成WebSocket聊天室 WebSocket特别的地方是 握手和消息内容的编码.解码(添加了ServerHelper协助处理) ServerHelper: using ...

  4. 分享基于 websocket 网页端聊天室

    博客地址:https://ainyi.com/67 有一个月没有写博客了,也是因为年前需求多.回家过春节的原因,现在返回北京的第二天,想想,应该也要分享技术专题的博客了!! 主题 基于 websock ...

  5. 基于 OpenResty 实现一个 WS 聊天室

    基于 OpenResty 实现一个 WS 聊天室 WebSocket WebSocket 协议分析 WebSocket 协议解决了浏览器和服务器之间的全双工通信问题.在WebSocket出现之前,浏览 ...

  6. FastAPI(56)- 使用 Websocket 打造一个迷你聊天室

    背景 在实际项目中,可能会通过前端框架使用 WebSocket 和后端进行通信 这里就来详细讲解下 FastAPI 是如何操作 WebSocket 的 模拟 WebSocket 客户端 #!usr/b ...

  7. 使用socket.io打造公共聊天室

    最近的计算机网络课上老师开始讲socket,tcp相关的知识,当时脑袋里就蹦出一个想法,那就是打造一个聊天室.实现方式也挺多的,常见的可以用C++或者Java进行socket编程来构建这么一个聊天室. ...

  8. nodejs+mongoose+websocket搭建xxx聊天室

    简介 本文是由nodejs+mongoose+websocket打造的一个即时聊天系统:本来打算开发一个类似于网页QQ类似功能的聊天系统,但是目前只是开发了一个模块功能 --- 类似群聊的,即一对多的 ...

  9. 利用socket.io+nodejs打造简单聊天室

    代码地址如下:http://www.demodashi.com/demo/11579.html 界面展示: 首先展示demo的结果界面,只是简单消息的发送和接收,包括发送文字和发送图片. ws说明: ...

随机推荐

  1. Java实现 LeetCode 700 二叉搜索树中的搜索(遍历树)

    700. 二叉搜索树中的搜索 给定二叉搜索树(BST)的根节点和一个值. 你需要在BST中找到节点值等于给定值的节点. 返回以该节点为根的子树. 如果节点不存在,则返回 NULL. 例如, 给定二叉搜 ...

  2. Java实现 蓝桥杯VIP 算法训练 P1102

    定义一个学生结构体类型student,包括4个字段,姓名.性别.年龄和成绩.然后在主函数中定义一个结构体数组(长度不超过1000), 并输入每个元素的值,程序使用冒泡排序法将学生按照成绩从小到大的顺序 ...

  3. Java实现汉诺塔问题

    1 问题描述 Simulate the movement of the Towers of Hanoi Puzzle; Bonus is possible for using animation. e ...

  4. TZOJ 复习时间

    描述 为了能过个好年,xhd开始复习了,于是每天晚上背着书往教室跑.为了追求更高的效率,xhd要根据难度值来选择合适的课程进行复习,复习后一门课的效率为前一门课之间的难度差的平方,而复习第一门课的效率 ...

  5. 【大厂面试04期】讲讲一条MySQL更新语句是怎么执行的?

    流程图 这是在网上找到的一张流程图,写的比较好,大家可以先看图,然后看详细阅读下面的各个步骤. 执行流程: 1.连接验证及解析 客户端与MySQL Server建立连接,发送语句给MySQL Serv ...

  6. Spring事务的传播属性

    前言 Spring在TransactionDefinition接口中规定了7种类型的事务传播行为.事务传播行为是Spring框架独有的事务增强特性,他不属于的事务实际提供方数据库行为.这是Spring ...

  7. 如何在本地搭建微信小程序服务器

    现在开发需要购买服务器,价格还是有点贵的,可以花费小代价就可以搭建一个服务器,可以用来开发小程序,博客等. 1.域名(备案过的) 2.阿里云注册免费的https证书 3.配置本地的nginx 4.内网 ...

  8. 1.Go 开始搞起

    link 1. IDE Go Land 服务器激活 2. 资源 中文网站 翻译组 翻译组wiki 待认领文章 入门指南 中文文档 fork 更新 github 中如何定期使用项目仓库内容更新自己 fo ...

  9. Netty 中的内存分配浅析

    Netty 出发点作为一款高性能的 RPC 框架必然涉及到频繁的内存分配销毁操作,如果是在堆上分配内存空间将会触发频繁的GC,JDK 在1.4之后提供的 NIO 也已经提供了直接直接分配堆外内存空间的 ...

  10. update语句基本用法

    UPDATE runoob_tbl SET runoob_title='学习 C++' WHERE runoob_id=;