热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:SpringBoot——SpringBoot集成WebSocket实现简单的多人聊天室

篇首语:本文由编程笔记#小编为大家整理,主要介绍了SpringBoot——SpringBoot集成WebSocket实现简单的多人聊天室相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了SpringBoot——SpringBoot集成WebSocket实现简单的多人聊天室相关的知识,希望对你有一定的参考价值。






文章目录:

1.什么是WebSocket?

2.Java中的WebSocket API

2.1 WebSocket开发中的相关注解及API方法

2.2 前端技术对WebSocket的支持

3.多人聊天室的实现源码

3.1 pom文件中添加相关依赖

2.2 在核心配置文件中配置视图解析器

2.3 加入相关静态资源文件

2.4 编写控制层controller

2.5 写一个配置类,开启SpringBoot对WebSocket的支持

2.6 写一个工具类

2.7 WebSocket的核心类

2.8 最后是我们的jsp页面

2.9 测试结果




1.什么是WebSocket?

WebSocket协议是由html5定义的,基于TCP协议实现的一种网络协议,通过该协议服务器可以主动向客户端发送信息;


WebSocket 协议在2008年诞生,2011年成为W3C国际标准;


我们已经有了 HTTP 协议,为什么出现一个websocket协议?


http协议是短连接,因为请求之后,都会关闭连接,下次重新请求数据,需要再次打开链接;


WebSocket协议是一种长连接,只需要通过一次请求来初始化连接,然后所有的请求和响应都是通过这个TCP连接进行通讯;


所以HTTP协议通信只能是客户端向服务器发出请求,服务器返回响应结果,HTTP 协议做不到服务器主动向客户端推送信息,而websocket能实现服务器和客户端全双工通信;


何谓全双工


信息只能单向传送为单工;信息能双向传送但不能同时双向传送称为半双工,信息能够同时双向传送则称为全双工;


基本实现原理


WebSocket协议基于TCP协议实现,客户端和服务器只需要做一个握手的动作之后,形成了一条基于客户端和服务器之间的快速通道,之后客户端与服务端之间便可以进行多次数据帧双向传输;


这样实现的目的是客户端和服务器进行频繁双向通信时,可以使服务器避免频繁创建HTTP连接,节约资源,提高工作效率和资源利用率;


传统Web推送实现


在没有WebSocket协议之前,服务器如何向浏览器端推送消息?


此时,通常的实现方式是在页面通过Ajax定时轮询,比如每隔1秒中向服务器发送一次HTTP请求,询问服务器是否有新消息,服务器返回结果;


这种形式缺点很明显,浏览器需要不断的向服务器发出HTTP请求,而HTTP请求包含较长的头部,有效信息相对较少,反复的无效请求占用了大量的带宽和 CPU 资源,造成很大的浪费,所以,WebSocket 应运而生;


HTML5定义的WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯;


WebSocket协议本质上是一个基于TCP的协议,因此与HTTP协议没有什么关系;


WebSocket的特点


全双工通信,客户端和服务器可以双向平等通信;


建立在TCP协议之上,服务器端的实现比较容易;


数据格式比较轻量,性能开销小,通信高效;


可以发送文本,也可以发送二进制数据;


通信具有更强的实时性;


协议标识符是ws,服务器地址举个例子就是:ws://www.abc.com/some/path


而http协议的地址则是: http://......


websocket业务场景


WebSocket聊天室;


股票实时价格显示等应用;


即时通讯、游戏、可视化大屏展示等领域;


企业内部管理通讯等功能,主要通讯协议websocket;


web网页聊天、客服系统实现;


系统提醒、用户上下线提醒、客户端同步,实时数据更新,多屏幕同步,用户在线状态,消息通知,扫描二维码登录/二维码支付,弹幕、各类信息提醒,在线选座,实时监控大屏等等;





2.Java中的WebSocket API

在Java EE 7中Java语言开始支持websocket协议,Java EE 7中定义了一套Websocket API规范,也就是一系列接口,没有实现,位于包javax.websocket下,包含客户端API和服务端API,WebSocket的Java API 只是规范,具体实现需要web容器(比如tomcat就实现了Java websocket api)、Java EE服务器或者框架提供;



javax.websocket

This package contains all the WebSocket APIs common to both the client and server side.

javax.websocket.server

This package contains all the WebSocket APIs used only by server side applications.



1、Tomcat:java中的websocket实现,需要tomcat 7.0.47+以上才支持Java EE7;


2、Spring的websocket,需要Spring 4.x,所以springboot也可以用;



2.1 WebSocket开发中的相关注解及API方法



@ServerEndpoint("/websocket/{uid}")


申明这是一个websocket服务;


需要指定访问该服务的地址,在地址中可以指定参数,需要通过{}进行占位;




@OnOpen


用法:public void onOpen(Session session, @PathParam("uid") String uid) throws IOException{}


该方法将在建立连接后执行,会传入session对象,就是客户端与服务端建立的长连接通道,通过@PathParam获取url中声明的参数;




@OnClose


用法:public void onClose() {}


该方法是在连接关闭后执行;




@OnMessage


用法:public void onMessage(String message, Session session) throws IOException {}


该方法用于接收客户端发送的消息;


message:发来的消息数据;


session:会话对象(也是长连接通道);


发送消息到客户端;


用法:session.getBasicRemote().sendText("hello,websocket.");


通过session进行消息发送;



2.2 前端技术对WebSocket的支持



Websocket是html5规范,主流浏览器都支持;(某些老浏览器不支持)


jQuery、vueJS、React JS、angularjs等都可以支持webscoket对象;


底层是Javascript支持的一个webscoket的js对象,通过这个对象可以建立websocket的连接:ws://localhost:8080/websocket/12345 





3.多人聊天室的实现源码

3.1 pom文件中添加相关依赖




org.springframework.boot
spring-boot-starter-web



org.springframework.boot
spring-boot-starter-websocket



org.apache.tomcat.embed
tomcat-embed-jasper



org.projectlombok
lombok
1.18.12






org.springframework.boot
spring-boot-maven-plugin





src/main/webapp
META-INF/resources

*.*




2.2 在核心配置文件中配置视图解析器

#配置视图解析器
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

2.3 加入相关静态资源文件


2.4 编写控制层controller



这其中只有一个请求,/chat,这个请求会跳转到我们的index.jsp页面。


package com.szh.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
*/
@Controller
public class ChatController {
//声明原子变量类,确保服务端和客户端之间操作的原子性和可见性
private AtomicInteger atomicInteger=new AtomicInteger();
@RequestMapping("/chat")
public String chat(Model model) {
model.addAttribute("username","user" + atomicInteger.getAndIncrement());
return "index";
}
}

2.5 写一个配置类,开启SpringBoot对WebSocket的支持

package com.szh.springboot.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
*
*/
@EnableWebSocket //开启SpringBoot对WebSocket的支持
@Configuration //声明该类是一个配置类
public class ChatConfig {
/**
* 配置ServerEndpointExporter的bean
* 该Bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
* @return
*/
@Bean
public ServerEndpointExporter serverEndpoint() {
return new ServerEndpointExporter();
}
}

2.6 写一个工具类



这个工具类封装了大量静态方法,供外界调用。


package com.szh.springboot.endpoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.RemoteEndpoint;
import javax.websocket.Session;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 聊天室功能实现的一个工具类
*/
public class ChatUtils {
//定义日志对象
private static final Logger logger= LoggerFactory.getLogger(ChatUtils.class);
//定义map集合,确保数据共享和安全,这里使用ConcurrentHashMap
//用户名为key,session信息为value
public static final Map CLIENTS&#61;new ConcurrentHashMap<>();
/**
* 使用连接发送消息
* &#64;param session 用户的session
* &#64;param message 发送的消息内容
*/
public static void sendMessage(Session session,String message) {
if (session &#61;&#61; null) {
return;
}
final RemoteEndpoint.Basic basic&#61;session.getBasicRemote();
if (basic &#61;&#61; null) {
return;
}
try {
basic.sendText(message);
} catch (IOException e) {
e.printStackTrace();
logger.error("sendMessage IOException",e);
}
}
/**
* 发送消息给所有人
* &#64;param message
*/
public static void sendMessageAll(String message) {
CLIENTS.forEach((sessionId,session) -> sendMessage(session,message));
}
/**
* 获取所有的在线用户
*/
public static String getOnlineInfo() {
Set userNames&#61;CLIENTS.keySet();
if (userNames.size() &#61;&#61; 0) {
return "当前无人在线......";
}
return userNames.toString() &#43; "在线";
}
}

2.7 WebSocket的核心类

package com.szh.springboot.endpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
/**
* &#64;ServerEndpoint注解中指定WebSocket协议的地址
* &#64;OnOpen、&#64;OnMessage、&#64;OnClose、&#64;OnError注解与WebSocket中监听事件对应
*/
&#64;Slf4j //生成一些日志代码
&#64;Component
&#64;ServerEndpoint("/websocket/{username}")
public class ChatServerEndpoint {
/**
* 连接建立时触发
*/
&#64;OnOpen
public void onOpen(&#64;PathParam("username") String username, Session session) {
log.info("用户{}登录",username);
String message&#61; "用户[" &#43; username &#43; "]已进入聊天室&#xff01;";
//将该用户登录的消息发送给其他人
ChatUtils.sendMessageAll(message);
//将自己的信息添加到map集合中
ChatUtils.CLIENTS.put(username,session);
//获取当前的在线人数&#xff0c;发给自己查看
String onlineInfo&#61;ChatUtils.getOnlineInfo();
ChatUtils.sendMessage(session,onlineInfo);
}
/**
* 客户端接收服务端发来的数据时触发
*/
&#64;OnMessage
public void onMessage(&#64;PathParam("username") String username,String message) {
log.info("发送消息&#xff1a;{}, {}",username,message);
//广播&#xff0c;把消息同步给其他客户端
ChatUtils.sendMessageAll("[" &#43; username &#43; "]: " &#43; message);
}
/**
* 连接关闭时触发
*/
&#64;OnClose
public void onClose(&#64;PathParam("username") String username,Session session) {
//从当前的map集合中移除该用户
ChatUtils.CLIENTS.remove(username);
//将该用户离线的消息通知给其他人
ChatUtils.sendMessageAll("[" &#43; username &#43; "]已离线&#xff01;");
try {
//关闭WebSocket下的该Seesion会话
session.close();
log.info("{} 已离线......",username);
} catch (IOException e) {
e.printStackTrace();
log.error("onClose error",e);
}
}
/**
* 聊天通信发生错误时触发
*/
&#64;OnError
public void onError(Session session,Throwable throwable) {
try {
//关闭WebSocket下的该Seesion会话
session.close();
} catch (IOException e) {
e.printStackTrace();
log.error("onError Exception",e);
}
log.info("Throwable msg " &#43; throwable.getMessage());
}
}

2.8 最后是我们的jsp页面



这里简单的说一下&#xff0c;当我们发起controller中对应的请求之后&#xff0c;会跳转到这个页面&#xff0c;待页面加载完之后&#xff08;就是执行完



张起灵在线聊天室


















2.9 测试结果



我这里开了三个用户&#xff0c;一个user0&#xff0c;一个user1&#xff0c;一个user2。


分别在浏览器中访问三次请求地址就可以开启三个用户的聊天室了。




然后我们关闭user2的窗口&#xff0c;或者点击离线按钮。




当user2离线之后&#xff0c;在user0和user1的聊天窗口中可以看到他的离线消息。




最后依次点击user1和user0的离线按钮&#xff0c;因为我们之前代码中用到了Slf4j&#xff0c;所以我们此时回到IDEA中查看控制台打印的日志信息。


 




推荐阅读
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • SpringBoot简单日志配置
     在生产环境中,只打印error级别的错误,在测试环境中,可以调成debugapplication.properties文件##默认使用logbacklogging.level.r ... [详细]
  • SpringMVC工作流程概述
    SpringMVC工作流程概述 ... [详细]
  • Tomcat安装与配置教程及常见问题解决方法
    本文介绍了Tomcat的安装与配置教程,包括jdk版本的选择、域名解析、war文件的部署和访问、常见问题的解决方法等。其中涉及到的问题包括403问题、数据库连接问题、1130错误、2003错误、Java Runtime版本不兼容问题以及502错误等。最后还提到了项目的前后端连接代码的配置。通过本文的指导,读者可以顺利完成Tomcat的安装与配置,并解决常见的问题。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
  • 本文介绍了ASP.NET Core MVC的入门及基础使用教程,根据微软的文档学习,建议阅读英文文档以便更好理解,微软的工具化使用方便且开发速度快。通过vs2017新建项目,可以创建一个基础的ASP.NET网站,也可以实现动态网站开发。ASP.NET MVC框架及其工具简化了开发过程,包括建立业务的数据模型和控制器等步骤。 ... [详细]
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • 本文介绍了在sqoop1.4.*版本中,如何实现自定义分隔符的方法及步骤。通过修改sqoop生成的java文件,并重新编译,可以满足实际开发中对分隔符的需求。具体步骤包括修改java文件中的一行代码,重新编译所需的hadoop包等。详细步骤和编译方法在本文中都有详细说明。 ... [详细]
  • 本文讨论了在ASP中创建RazorFunctions.cshtml文件时出现的问题,即ASP.global_asax不存在于命名空间ASP中。文章提供了解决该问题的代码示例,并详细解释了代码中涉及的关键概念,如HttpContext、Request和RouteData等。通过阅读本文,读者可以了解如何解决该问题并理解相关的ASP概念。 ... [详细]
author-avatar
猪的快乐旅途_278
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有