源码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. 清晰明亮的白色lua协程(coroutine)

    协同程序线程类和多线程下似:它有它自己的堆栈.自己的局部变量.它有自己的指令指针,但是,其他协程共享全局变量和其他项目信息.主要不同在于:多处理器的情况下.概念上来说多线程是同一时候执行多个线程,而协 ...

  2. C# VS 2010创建、安装、调试 windows服务(windows service)

    在一个应用程序中创建多个 windows 服务的方法和 1083 的解决办法 错误解决方案 ------------------------------------------------------ ...

  3. Swift程式语言(中国版)(8.8 %)

    前言 今天Apple宣布了一项新的编程语言Swift.还提供了一个近400页The Swift Programming Language(Swift程式语言). 虽然我没有开发者账户.不能实际锻炼机S ...

  4. thread、Task、async & await

    学习 Jesse 的文章 async & await 的前世今生(Updated) 而来 Thread是最开始使用的多线程.new一个Thread对象,将方法传进去.手动Start() 还可以 ...

  5. Cocos2d-x3.1下实现相似iOS页面滑动指示圆点

    原文地址:http://blog.csdn.net/qqmcy/article/details/37612457 代码下载:http://download.csdn.net/detail/qqmcy/ ...

  6. MySQL—FTS实现原理介绍PPT

    这个PPT是有一天我要给同事讲解MySQL的FTS的实现原理花了一个小时做的.

  7. WPF Timeline简易时间轴控件的实现

    原文:WPF Timeline简易时间轴控件的实现 效果图: 由于整个控件是实现之后才写的教程,因此这里记录的代码是最终实现后的,前后会引用到其他的一些依赖属性或者代码,需要阅读整篇文章. 1.确定T ...

  8. 【转】postgresql 9.4 在linux环境的安装步骤详解

    本文章来为各位介绍一篇关于postgresql 9.4 在linux环境的安装步骤详解,希望文章能够对各位新手朋友带来帮助的哦. 环境说明系统:centos 6.4 64位软件:postgresql ...

  9. 梧桐那时雨http://blog.csdn.net/fuchaosz/article/details/51882935?readlog

    Ubuntu 16.04 一系列软件安装命令,包括QQ.搜狗.Chrome.vlc.网易云音乐安装方法 原创 2016年07月20日 11:44:01 标签: ubuntu 27024 1 简介 Ub ...

  10. CentOS搭建python开发环境

    装了个CentOS 5.5,想在上面搭个python的开发环境,可是还是遇到了很多问题,记录一下过程: 1.python升级 查看python版本 python -V Python 2.4.3 因为p ...