源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all

一、说明

1.1 项目结构说明

  1. 项目模拟一个简单的群聊功能,为区分不同的聊天客户端,登录时候将临时用户名存储在session当中;
  2. CustomHander为消息的自定义处理器;
  3. CustomHandershakerInterceptor为自定义的 websocket 的握手拦截器;
  4. 项目以web的方式构建。

1.2 依赖说明

除了基本的spring 依赖外,还需要导入webSocket的依赖包

 <!--spring webSocket 的依赖包 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>5.1.3.RELEASE</version>
</dependency>

二、spring websocket

2.1 创建消息处理类,继承自TextWebSocketHandler

/**
 * @author : heibaiying
 * @description : 自定义消息处理类
 */
public class CustomHandler extends TextWebSocketHandler {

    private Map<String, WebSocketSession> nameAndSession = new ConcurrentHashMap<>();

    // 建立连接时候触发
    @Override
    public void afterConnectionEstablished(WebSocketSession session)  {
        String username = getNameFromSession(session);
        nameAndSession.putIfAbsent(username, session);
    }

    // 关闭连接时候触发
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        String username = getNameFromSession(session);
        nameAndSession.remove(username);
    }

    // 处理消息
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 防止中文乱码
        String msg = URLDecoder.decode(message.getPayload(), "utf-8");
        String username = getNameFromSession(session);
        // 简单模拟群发消息
        TextMessage reply = new TextMessage(username + " : " + msg);
        nameAndSession.forEach((s, webSocketSession)
                -> {
            try {
                webSocketSession.sendMessage(reply);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    private String getNameFromSession(WebSocketSession session) {
        Map<String, Object> attributes = session.getAttributes();
        return (String) attributes.get(Constant.USER_NAME);
    }
}

2.2 创建websocket 握手拦截器(如果没有权限拦截等需求,这一步不是必须的)

/**
 * @author : heibaiying
 * @description : 可以按照需求实现权限拦截等功能
 */
public class CustomHandshakeInterceptor extends HttpSessionHandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        InetAddress address = remoteAddress.getAddress();
        System.out.println(address);
        /*
         * 最后需要要显示调用父类方法,父类的beforeHandshake方法
         * 把ServerHttpRequest 中session中对应的值拷贝到WebSocketSession中。
         * 如果我们没有实现这个方法,我们在最后的handler处理中 是拿不到 session中的值
         * 作为测试 可以注释掉下面这一行 可以发现自定义处理器中session的username总是为空
         */
        return super.beforeHandshake(request, response, wsHandler, attributes);
    }
}

2.3 配置websocket

<?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:mvc="http://www.springframework.org/schema/mvc"
       xmlns:websocket="http://www.springframework.org/schema/websocket"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
        http://www.springframework.org/schema/websocket
        http://www.springframework.org/schema/websocket/spring-websocket.xsd">

    <!-- 开启注解包扫描-->
    <context:component-scan base-package="com.heibaiying.*"/>

    <!--使用默认的Servlet来响应静态文件 -->
    <mvc:default-servlet-handler/>

    <!-- 开启注解驱动 -->
    <mvc:annotation-driven/>

    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          id="internalResourceViewResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 后缀 -->
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--配置webSocket-->
    <bean id="customHandler" class="com.heibaiying.websocket.CustomHandler"/>
    <websocket:handlers>
        <!--指定webSocket 地址-->
        <websocket:mapping path="/socket" handler="customHandler"/>
        <!--webSocket握手-->
        <websocket:handshake-interceptors>
            <bean class="com.heibaiying.websocket.CustomHandshakeInterceptor"/>
        </websocket:handshake-interceptors>
    </websocket:handlers>

</beans>

2.4 前端 websocket 的实现

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>${sessionScope.get("username")}您好!欢迎进入群聊大厅!</title>
</head>
<body>
<input id="message" type="text">
<button id="btn">发送消息</button>
<div id="show">

</div>
<script>
    let btn = document.getElementById("btn");
    let message = document.getElementById("message");
    let show = document.getElementById("show");
    let ws = new WebSocket("ws://localhost:8080/socket");
    ws.onmessage = function (evt) {
        let node = document.createElement("div");
        node.innerHTML = "<h5>" + evt.data + "</h5>";
        show.appendChild(node);
    };
    btn.addEventListener("click", function () {
        let data = message.value;
        console.log(data);
        if (data) {
            ws.send(encodeURI(data));
        } else {
            alert("请输入消息后发送");
        }
        message.value = "";
    });
    // 关闭页面时候关闭ws
    window.addEventListener("beforeunload", function(event) {
        ws.close();
    });
</script>
</body>
</html>

2.5 简单登录的实现

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/login" method="post">
    <input name="username" type="text">
    <button id="btn">输入临时用户名后登录!</button>
</form>
</body>
</html>
@Controller
public class LoginController {

    @PostMapping("login")
    public String login(String username, HttpSession session){
        session.setAttribute(Constant.USER_NAME,username);
        return "chat";
    }
}

附:源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all

spring 5.x 系列第17篇 —— 整合websocket (xml配置方式)的更多相关文章

  1. spring 5.x 系列第18篇 —— 整合websocket (代码配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.说明 1.1 项目结构说明 项目模拟一个简单的群聊功能,为区分不同的聊 ...

  2. spring 5.x 系列第13篇 —— 整合RabbitMQ (xml配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.说明 1.1 项目结构说明 本用例关于rabbitmq的整合提供简单消 ...

  3. spring 5.x 系列第11篇 —— 整合memcached (xml配置方式)

    文章目录 一.说明 1.1 XMemcached客户端说明 1.2 项目结构说明 1.3 依赖说明 二.spring 整合 memcached 2.1 单机配置 2.2 集群配置 2.3 存储基本类型 ...

  4. spring 5.x 系列第9篇 —— 整合mongodb (xml配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.说明 1.1 项目结构说明 配置文件位于resources下,项目以单 ...

  5. spring 5.x 系列第15篇 —— 整合dubbo (xml配置方式)

    文章目录 一. 项目结构说明 二.项目依赖 三.公共模块(dubbo-common) 四. 服务提供者(dubbo-provider) 4.1 productService是服务的提供者( 商品数据用 ...

  6. spring 5.x 系列第1篇 —— springmvc基础 (xml配置方式)

    文章目录 一.搭建hello spring工程 1.1 项目搭建 1.2 相关配置讲解 二.配置自定义拦截器 三.全局异常处理 四.参数绑定 4.1 参数绑定 4.2 关于日期格式转换的三种方法 五. ...

  7. spring 5.x 系列第14篇 —— 整合RabbitMQ (代码配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.说明 1.1 项目结构说明 本用例关于rabbitmq的整合提供简单消 ...

  8. spring 5.x 系列第12篇 —— 整合memcached (代码配置方式)

    文章目录 一.说明 1.1 XMemcached客户端说明 1.2 项目结构说明 1.3 依赖说明 二.spring 整合 memcached 2.1 单机配置 2.2 集群配置 2.3 存储基本类型 ...

  9. spring 5.x 系列第10篇 —— 整合mongodb (代码配置方式)

    源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all 一.说明 1.1 项目结构说明 配置文件位于com.heibaiying. ...

随机推荐

  1. dos命令查看端口状态

    netstat 显示协议统计信息和当前 TCP/IP 网络连接. NETSTAT [-a] [-b] [-e] [-n] [-o] [-p proto] [-r] [-s] [-v] [interva ...

  2. 使用WPF将图片转变为灰度并加上水印并保存为文件

    原文:使用WPF将图片转变为灰度并加上水印并保存为文件 运行效果: (上图中左下角为原图的缩小显示,By:Johnson为TextBlock)保存的结果图片:上图的"Test Words.& ...

  3. [Songqw.Net 基础]WPF插件化中同步Style

    原文:[Songqw.Net 基础]WPF插件化中同步Style 版权声明:本文为博主原创文章,未经博主允许可以随意转载 https://blog.csdn.net/songqingwei1988/a ...

  4. Hadoop入门实验

    一.实验目的 了解Hadoop的MapeReduce工作原理 二.实验内容 实现基于单机的伪分布式运行模拟 三.实验需要准备的软件和源 1.Jdk1.6以上 下载地址:http://www.oracl ...

  5. zend-form笔记

    Zend-Form组件包含以下几个对象: 1.Elements:包含了name和attributes, 2.Fieldsets:继承自elements,但允许包含其他fieldset和elements ...

  6. NPOI在无Office环境下,对Office文件的操作

    在做项目的时候,经常会遇到对 Office的操作,但有时候会没有Office环境,因此给大家介绍一个思路,在没有Office环境下,对Office的处理. NPOI,顾名思义,就是POI的.NET版本 ...

  7. WPF 中style文件的引用

    原文:WPF 中style文件的引用 总结一下WPF中Style样式的引用方法: 一,内联样式: 直接设置控件的Height.Width.Foreground.HorizontalAlignment. ...

  8. PHP 一致性Hash

    一致性HASH 好久没有写文章了,最近忙着公司的事情,也一拖再拖.这篇一致性hash是很久之前就有的一篇算法,记录一下,这周写个基于该算法的Redis中间件. HASH算法的精髓就在于打散原本杂乱无序 ...

  9. C# 获得设备usb信息

    原文:C# 获得设备usb信息 本文告诉大家如何获得设备的usb来进行判断是否有哪些usb和找不到usb可能是什么. 需要在项目右击引用,点击程序集,搜索 System.Management 然后安装 ...

  10. [shell]流程控制----case语句

    Shell case语句为多选择语句.可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令.case语句格式如下: case 值 in 模式1) command1 command2 ...