Spring 学习——基于Spring WebSocket 和STOMP实现简单的聊天功能

本篇主要讲解如何使用Spring websocket 和STOMP搭建一个简单的聊天功能项目,里面使用到的技术,如websocket和STOMP等会简单介绍,不会太深,如果对相关介绍不是很了解的,请自行查阅相关知识。 本篇的项目主要是一个学习Spring websocket和STOMP的项目,基于Spring4.0之上。因为Spring4.0之上才支持Websocket。例子比较的简单,但是总体实

大家好,又见面了,我是全栈君。

本篇主要讲解如何使用Spring websocket 和STOMP搭建一个简单的聊天功能项目,里面使用到的技术,如websocket和STOMP等会简单介绍,不会太深,如果对相关介绍不是很了解的,请自行查阅相关知识。
本篇的项目主要是一个学习Spring websocket和STOMP的项目,基于Spring4.0之上。因为Spring4.0之上才支持Websocket。例子比较的简单,但是总体实现了

  1. 浏览器 和服务器可以正常建立websocket服务
  2. 浏览器可以像服务器订阅并发送消息
  3. 浏览器可以接收到服务器推送过来的消息,即浏览器订阅的消息

一.简单介绍Websocket和STOMP

1.websocket介绍

websocket协议是html5的一种新的协议,提供了通过套接字实现全双工通信的功能,【全双工:意味着服务器可以发送消息给浏览器,浏览器也可以发送消息给服务器】并能够实现web浏览器和服务器之间的异步通信。

它和http通信机制对比如下图:

这里写图片描述

  • http每次发送请求都需要和服务器建立一次连接, 是一种无状态的协议。
  • websocket只要第一个建立成功,浏览器就可以服务器进行通信,是一种有状态的协议。
  • websocket协议的请求报文和响应报文和http也是有区别的,这里不做介绍!

2.STOMP介绍

什么是 STOMP呢?
http是在TCP套接字之上添加了请求-响应模型!
STOMP是在WebSocket之上提供了一个基于帧的线路格式层,用于定义消息的语义。
STOMP帧由命令、一个或多个头信息以及负载所组成!举例发送数据的一个STOMP帧:

SEND
destination:/app/marco
content-length:20

{ 
   \"message\":\"hello\"}
  • 这里STOMP的命令是SEND,后面接发送的目标地址,消息内容长度,然后是一个空行,最后是发送内容,这个里面是一个JSON消息。
  • 这里需要注意的是destination,目标地址,消息会发送到这个目的地,这个目的地有服务端组件来进行处理。
  • Spring使用STOMP需要进行配置,并且Spring为STOMP消息提供了基于SpringMVC的编程模型!

二.搭建项目【项目的源码 springwebscoket demo

下面进入实战演练:

1、添加依赖

<!-- 添加Spring依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context-support</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--spring单元测试依赖 -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>${spring.version}</version>
      <scope>test</scope>
    </dependency>

    <!-- spring webmvc相关jar -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>${spring.version}</version>
    </dependency>

    <!--Spring websocket start-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-websocket</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-messaging</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--Spring websocket end-->

    <!-- For SockJS -->
    <!-- http://jira.codehaus.org/browse/JACKSON-884 -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>${jackson.version}</version>
    </dependency>

2、添加配置文件

1.web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
  <display-name>spring-webscoket</display-name>
  <!-- 读取spring配置文件 -->


    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:application*.xml</param-value>
    </context-param>


    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!-- Spring字符集过滤器 -->
    <filter>
        <filter-name>SpringEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <async-supported>true</async-supported>
    </filter>
    <filter-mapping>
        <filter-name>SpringEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>



    <!-- springMVC核心配置 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <!--spingMVC的配置路径 -->
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    <!-- 拦截设置 -->
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>


  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
</web-app>

需要注意:< async-supported>true< /async-supported>,在filter和Servlet中都需要添加!

2.spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

    <!-- 扫描controller(controller层注入) -->
   <context:component-scan base-package="com.dufy"/>

     <mvc:annotation-driven />

    <!-- 当在web.xml 中 DispatcherServlet使用 <url-pattern>/</url-pattern> 映射时,能映射静态资源 -->
    <mvc:default-servlet-handler />  
    <!-- 静态资源映射 -->
    <mvc:resources mapping="/static/**" location="/WEB-INF/static/"/>


   <!-- 对模型视图添加前后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>


</beans>
3.applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd ">

    <!-- Spring WebSocketSession管理容器 -->
    <bean id="webSocketSessionManager" class="com.dufy.webscocket.session.data.DefaultWebSocketSessionManager" />

    <!-- websocket握手拦截器 -->
    <bean id="handshakeInterceptor" class="com.dufy.webscocket.interceptor.ChatHandInteceptor" />

    <!-- WebSocket相关监听器 -->
    <bean id="sessionConnectedListener" class="com.dufy.webscocket.listener.SessionConnectedListener" />
    <bean id="sessionDisConnectedListener" class="com.dufy.webscocket.listener.SessionClosedListener" />
    <bean id="sessionSubscribeListener" class="com.dufy.webscocket.listener.SessionSubscribeListener" />

</beans>

3、添加代码

1.后台代码目录

这里写图片描述

2.前台代码

(1)建立websocket的jsp页面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<% String path = request.getContextPath(); String basePath = request.getServerName() + ":" + request.getServerPort(); String finalPath = "http://" + basePath + path; out.print("finalPath = " + finalPath); %>
<html>
<head>

    <script src="${pageContext.request.contextPath}/recourse/jquery-1.7.2.min.js"></script>
    <script src="${pageContext.request.contextPath}/recourse/stomp.js"></script>
    <script src="${pageContext.request.contextPath}/recourse/sockjs.min.js"></script>
    <script type="text/javascript"> $(document).ready(function(){ 
     connect(); //checkoutUserlist(); }); var projectName = "springwebscoket"; var baseUrl= getBaseUrl(); var stompClient = null; console.log("baseUrl = " + baseUrl); //this line. function connect() { 
     console.log('Connected: ----------------------------------'); var userid = document.getElementById('name').value; var socket = new SockJS(baseUrl + projectName +"/chat"); console.log("=-="+ socket); stompClient = Stomp.over(socket); stompClient.connect({}, function(frame) { 
     setConnected(true); console.log('Connected: ' + frame); stompClient.ws.onclose = function (CloseEvent){ 
     console.log("ERROR","weboscket close code is " + CloseEvent.code); setConnected(false); }; //系统订阅消息 stompClient.subscribe('/user/queue/system/newMsg', function (greeting) { 
     console.log("------ue/system/newMsg-----------"); console.log(greeting); showGreeting(JSON.parse(greeting.body).content); }); //新消息订阅 stompClient.subscribe('/user/queue/chat/newMsg', function (greeting) { 
     console.log("/chat/newMsg" + greeting); $("#showMessage").append("<p>"+JSON.parse(greeting.body).message+"</p>") //showGreeting(JSON.parse(greeting.body).content); }); //访客消息响应订阅 stompClient.subscribe('/user/queue/chat/msgResponse', function (greeting) { 
     alert("收到信息"); }); //关闭订阅消息 stompClient.subscribe('/user/queue/system/close', function (greeting) { 
     var retCode = JSON.parse(greeting.body).retCode; if(retCode == "000000"){ _evaluate.open(); disconnect(); } }); },function(error){ 
     var msg = "会话建立失败,请稍候重试!"; alert(msg); setConnected(false); }); } function sendName() { 
     var name = document.getElementById('name').value; stompClient.send("/app/sendMessage", {}, JSON.stringify({ 'name': name })); } function disconnect() { 
     if (stompClient != null) { stompClient.disconnect(); } setConnected(false); console.log("Disconnected"); } function setConnected(connected) { 
     document.getElementById('connect').disabled = connected; document.getElementById('disconnect').disabled = !connected; document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden'; document.getElementById('response').innerHTML = ''; } function showGreeting(message) { 
     var response = document.getElementById('response'); var p = document.createElement('p'); p.style.wordWrap = 'break-word'; p.appendChild(document.createTextNode(message)); response.appendChild(p); } /** * 获取baseUrl * @returns {string} */ function getBaseUrl(){ 
     var url= window.location.href; var num = url.indexOf(projectName); var baseUrlStr = url.substring(0,num); return baseUrlStr; } </script>
</head>
<body>
<h1>Welcome</h1> ${name }<h1>访问此页面</h1>
<div>
    <div>
        <button id="connect" onclick="connect();">Connect</button>
        <button id="connectAny" onclick="connectAny();">ConnectAny</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
    </div>
    <div id="conversationDiv">
        <label>you can send message to WebSocketMessageController[ @MessageMapping("/sendMessage") ]</label><input type="text" id="name" />
        <button id="sendName" onclick="sendName();">Send</button>
        <p id="response"></p>
    </div>

    <div id="showMessage">

    </div>

</div>
</div>
</body>
</html>

(2)服务器推送消息的页面:

<%@ page contentType="text/html; charset=utf-8"%>
<!doctype html>
<html>
<head>
    <title>agent page</title>
    <script src="${pageContext.request.contextPath}/recourse/jquery.js"></script>
</head>
<body>
    <div>
        <input type="text" placeholder="请输入wsSocketid" id="webSocketId" value="${wsId}"/><br/>
        <input type="text" placeholder="请输入发送的内容" id="message"/><br/>
        <button id="sendMsg">发送</button>
        <button id="resetMessage">清空发送内容</button>
        <button id="resetid">重置id</button>
    </div>

</body>
<script> $("#sendMsg").click(function () { 
     var webSocketId = $("#webSocketId").val(); var message = $("#message").val(); var url = "${pageContext.request.contextPath}/websocket/notifyMsg"; $.post(url,{ 
   "webSocketId":webSocketId,"message":message},function (data) { 
     console.log(data); alert(data); }) }) $("#resetid").click(function () { 
     $("#webSocketId").val(""); }) $("#resetMessage").click(function () { 
     $("#message").val(""); }) </script>
</html>

4、项目演示

(1).启动项目,我项目 不是的Application context 为 /springwebscoket ,端口9091,启动成功访问:http://localhost:9081/springwebscoket/,显示如下页面:
这里写图片描述

(2).点击访客登录,进行登录,登录成功!创建websocket成功!如图:
这里写图片描述

(3).创建成功,发送消息到服务器,如图:
这里写图片描述

(4).创建成功,接收服务器发来的消息,如图:
这里写图片描述

三.总结

上面的项目里面的代码没有具体进行讲解,因为项目中可以运行起来,并且里面的逻辑也比较简单,如有不懂的地方可以直接对项目进行debug调试,一步步看,一步步学习,建议学习之前,要对websocket和STOMP有了解!
如果您对项目中有不懂的地方,欢迎加入我的QQ群:600756207,和我进行沟通!共同成长!

四.参考

官方文档


欢迎访问我的csdn博客,我们一同成长!

不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!

博客首页http://blog.csdn.net/u010648555

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/121143.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 文件删除不掉:0X80070570解决办法

    文件删除不掉:0X80070570解决办法nbsp nbsp nbsp nbsp nbsp 今天在 linux 下拷东西 因为有急事所以强制拔掉了硬盘 结果就出现文件夹删不掉的情况 采用 Ultral 试试的时候获得了错误码 0X 这样就好办了 搜索了几下就得到了解决办法 右键硬盘选择工具检查磁盘 然后再进入硬盘删除该文件夹 成功解决 其实很简单

    2025年7月8日
    1
  • 怎么html文字下划线,HTML怎么设置下划线?html文字加下划线方法

    怎么html文字下划线,HTML怎么设置下划线?html文字加下划线方法HTML中的下划线曾经是将文本包含在标签中的问题,但是这种方法已被放弃,而更倾向于使用更多功能的CSS。一般来说,下划线被认为是引起人们对文本注意的一种方式,那么HTML怎么设置下划线?html文字加下划线方法?下面我们来总结一下。1.使用“text-decoration”CSS样式属性,使用标签不再是强调文本的正确方法。而是使用“text-decoration”CSS属性,语法为:<sp…

    2022年6月3日
    48
  • day72Django

    day72Django

    2021年6月15日
    105
  • 重定向与转发的区别以及实现_重定向与转发

    重定向与转发的区别以及实现_重定向与转发一、转发和重定向的区别request.getRequestDispatcher()是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;服务器内部转发,整个过程处于同一个请求当中。response.sendRedirect()则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。不在同一个请求。重定向,实际上客户端会向服务器端发送两个请求。所以转发中数据的存取可以用request作用域:request.setAtt…

    2025年7月11日
    3
  • pycharm中彻底删除一个工程的步骤

    pycharm中彻底删除一个工程的步骤具体出现的问题是,你已经删除的工程反复出现在pycharm里面。解决步骤:1,打开pycharm,点击File——>CloseProjects(有的是CloseProjectsinCurrentWindow);2,然后会出现一个小窗口,左边一列为你的工程项目,选择想删除的项目,点击右上角的叉号;3,最后打开你想删除的工程项目的文件路径,左键点击选中该项目,然后按…

    2022年8月29日
    5
  • 【机器学习】代价函数,损失函数,目标函数区别

    【机器学习】代价函数,损失函数,目标函数区别一:损失函数,代价函数,目标函数定义首先给出结论:损失函数(LossFunction)是定义在单个样本上的,算的是一个样本的误差。代价函数(CostFunction)是定义在整个训练集上的,是所有样本误差的平均,也就是损失函数的平均。目标函数(ObjectFunction)定义为:最终需要优化的函数。等于经验风险+结构风险(也就是CostFunction+正则化项)。关于目标函数和…

    2022年4月29日
    124

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号