太赞了,竟然用SpringBoot打造一款网页版的IM,进行聊天...



目录
传统 Tomcat 开发 WebSocket 回顾
SpringBoot 整合 Tomcat  WebSocket
websocket 通信测试
传统 Tomcat 开发 WebSocket 回顾
WebSocket 的出现是基于 Web 应用的实时性需要而产生的,在淘宝、京东等网页客服、网页卖家聊天等需求上应用广泛。对于前端网页可以使用 H5 开发 WebSocket 客户端,也可以使用 SockJS 库开发 WebSocket 客户端。
对于Java 开发者而言,后台 WebSocket 服务端开发通常有以下常用的选择:
Tomcat7 以后开始支持 websocket 协议
Spring4 以后开始支持 WebSocket
Netty3 以后支持开发 WebSocket
Tomcat8 如下所示,自身已经支持 WebSocket 服务端开发,它的 lib 目录下有自己实现 WebSocket 协议的开发包,如果是传统的 Java Web 项目,则只需要将 tomcat-websocket.jar、websocket-api.jar 导入应用中即可进行代码开发。
Tomcat 自身也提供了 WebSocket 开发的示例,在 webapps/exampls下,一共提供了 4 个示例,可以启动 Tomcat 进行访问测试以及学习:


对于传统 Java Web 导包式应用开发,这里不再过多进行说明,它的基本流程是:
1)新建 Java Web 应用后,导入 Tomcat 服务器 lib 目录下的 websocket-api.jar 、tomcat-websocket.jar开发包,前者是浏览器 webSocket 规范的接口,后者是 Tomcat 对它的实现
2)创建后台 webSocket 服务端类,标识 @ServerEndpoint( javax.websocket.server.ServerEndpoint)注解,表示当前类是 webSocket 服务终端,同时在里面实现客户端连接建立、发送消息、接收消息等通信业务。
3)自己实现 javax.websocket.server.ServerApplicationConfig 接口,扫描整个应用所有的 @ServerEndpoint 服务终端SpringBoot 整合 Tomcat  WebSocket
本文的重点是 SpringBoot 项目如何使用 Tomcat 的 webSocket 服务端开发;本文环境:springboot 2.1.0 ,使用内置的 Tomcat 服务器。
Spring boot 创建的 web 应用,web 启动模块已经依赖了 tomcat-websocket 模块,所以不需要再重复导入,但是必须导入 Spring boot 的 websocket 启动模块 spring-boot-starter-websocket。
<!-- Spring boot WebSocket--><dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-websocket</artifactId></dependency>@ServerEndpoint 创建 websocket 服务终端
1、创建后台 webSocket 服务端类,标识 @ServerEndpoint( javax.websocket.server.ServerEndpoint)注解,表示当前类是 webSocket 服务终端,同时在里面实现客户端连接建立、发送消息、接收消息等通信业务。
2、这与传统导包式开发 Tomcat WebSocket 服务端是一样的,区别就是:传统方式 @ServerEndpoint 类上不需要加 @Component 交由 Spring 管理,而现在需要加上 @Component 将此组件交由 spring 管理。
import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import javax.websocket.*;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.HashSet;import java.util.Set;/** * Created by Administrator on 2018/11/28 0028. * @ServerEndpoint :标识此类为 Tomcat 的 websocket 服务终端,/websocket/yy.action 是客户端连接请求的路径 * @Component :将本类交由 spring IOC 容器管理 */@ServerEndpoint(value = "/websocket/yy.action")@Componentpublic class ServerEnpoint {    private static Logger logger = LoggerFactory.getLogger(ServerEnpoint.class);    /**     * 用 Set 来 存储 客户端 连接     */    private static Set<Session> sessionSet = new HashSet<>();    /**     * 连接成功后自动触发     *     * @param session     */    @OnOpen    public void afterConnectionEstablished(Session session) {        /**         * session 表示一个连接会话,整个连接会话过程中它都是固定的,每个不同的连接 session 不同         * String queryString = session.getQueryString();//获取请求地址中的查询字符串         * Map<String, List<String>> parameterMap = session.getRequestParameterMap();//获取请求地址中参数         * Map<String, String> stringMap = session.getPathParameters();         * URI uri = session.getRequestURI();         */        sessionSet.add(session);        logger.info("新客户端加入,session id=" + session.getId() + ",当前客户端格个数为:" + sessionSet.size());         /**         * session.getBasicRemote().sendText(textMessage);同步发送         * session.getAsyncRemote().sendText(textMessage);异步发送         */        session.getAsyncRemote().sendText("我是服务器,你连接成功!");    }    /**     * 连接断开后自动触发,连接断开后,应该清楚掉 session 集合中的值     *     * @param session     */    @OnClose    public void afterConnectionClosed(Session session) {        sessionSet.remove(session);        logger.info("客户端断开,session id=" + session.getId() + ",当前客户端格个数为:" + sessionSet.size());    }    /**     * 收到客户端消息后自动触发     *     * @param session     * @param textMessage :客户端传来的文本消息     */    @OnMessage    public void handleMessage(Session session, String textMessage) {        try {            logger.info("接收到客户端信息,session id=" + session.getId() + ":" + textMessage);            /**             * 原样回复文本消息             * getBasicRemote:同步发送             * session.getAsyncRemote().sendText(textMessage);异步发送             * */            session.getBasicRemote().sendText(textMessage);        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * 消息传输错误后     *     * @param session     * @param throwable     */    @OnError    public void handleTransportError(Session session, Throwable throwable) {        System.out.println("shake client And server handleTransportError,session.getId()=" + session.getId() + " -- " + throwable.getMessage());        logger.error("与客户端 session id=" + session.getId() + " 通信错误...");    }}注入 ServerEndpointExporter
1、注入 org.springframework.web.socket.server.standard.ServerEndpointExporter,这个 bean 会自动注册使用了@ServerEndpoint 注解声明的 Websocket endpoint 。
2、如果使用独立的 servlet 容器,而不是使用 spring boot 的内置容器,就不要注入ServerEndpointExporter,因为它将由 Tomcat 容器自己提供和管理。
3、因为传统导包式 Tomcat websocket 开发时,是需要实现 javax.websocket.server.ServerApplicationConfig 接口的,然后由它去扫描整个应用中的 @ServerEndpoint,而现在这一步就由 springboot 的 ServerEndpointExporter 取代了。
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;/** * Created by Administrator on 2018/11/28 0028. */@Configurationpublic class WebSocketConfig {    /**     * 创建 ServerEndpointExporter 组件,交由 spring IOC 容器管理,     * 它会自动扫描注册应用中所有的 @ServerEndpoint     *     * @return     */    @Bean    public ServerEndpointExporter serverEndpointExporter() {        return new ServerEndpointExporter();    }}前端 H5 webSocket 客户端
为了方便,直接使用 H5 的 webSocket 方式,页面的 html 与 css 样式就不做提供了,直接提供客户端 webSocket 的 js 代码。
/** * web socket 绑定 */var ws = null;function webSocketBind() {    /**主流浏览器现在都支持 H5 d的 webSocket 通信,但建议还是要判断*/    if ("WebSocket" in window) {        /**创建 web socket 实例         * 如果连接失败,浏览器控制台报错,连接失败         * 前缀 ws:// 必须正确,yyServer 是应用名称,websocket/yy.action 是后台访问路径         * 192.168.1.20:websocket 服务器地址         * */        ws = new WebSocket("ws://192.168.1.20/yyServer/websocket/yy.action");         /**onopen:服务器连接成功后,自动触发*/        ws.onopen = function () {            /** Web Socket 已连接上,使用 send() 方法发送数据*/                //ws.send("connect success...");            console.log("服务器连接成功,并发送数据到后台...");        };         /**服务器发送数据后,自动触发此方法,客户端进行获取数据,使用 evt.data 获取数据*/        ws.onmessage = function (evt) {            var received_msg = evt.data;            console.log("接收到服务器数据:" + received_msg);            showClientMessage(received_msg);        };         /**客户端与服务器数据传输错误时触发*/        ws.onerror = function (evt) {            console.log("客户端 与 服务器 数据传输错误...");        };         /**web Socket 连接关闭时触发*/        ws.onclose = function () {            console.log("web scoket 连接关闭...");        };    } else {        alert("您的浏览器不支持 WebSocket!");    }} /** * 显示服务器发送的消息 * @param message */let showServerMessage = function (message) {    if (message != undefined && message.trim() != "") {        /**         * 往服务器发送消息         */        ws.send(message.trim());        /**         * scrollHeight:div 区域内文档的高度,只能 DOM 操作,JQuery 没有提供相应的方法         * @type {string}         */        let messageShow = "<div class='messageLine server'><div class='messageContent serverCon'>" + message + "</div><span>:我</span>";        $(".centerTop").append(messageShow + "<br>");        $(".messageArea").val("");         let scrollHeight = $(".centerTop")[0].scrollHeight;        $(".centerTop").scrollTop(scrollHeight - $(".centerTop").height());    }}; /** * 显示客户端的消息 * @param message */let showClientMessage = function (message) {    if (message != undefined && message.trim() != "") {        /**         * scrollHeight:div 区域内文档的高度,只能 DOM 操作,JQuery 没有提供相应的方法         * @type {string}         */        let messageShow = "<div class='messageLine client'><span>服务器:</span><div class='messageContent clientCon'>" + message + "</div>";        $(".centerTop").append(messageShow + "<br>");        $(".messageArea").val("");         let scrollHeight = $(".centerTop")[0].scrollHeight;        $(".centerTop").scrollTop(scrollHeight - $(".centerTop").height());    }}; $(function () {    /**初始化后清空消息发送区域*/    $(".messageArea").val("");     /**     * 为 消息 发送按钮绑定事件     */    $(".sendButton").click(function () {        let message = $(".messageArea").val();        showServerMessage(message);    });     /**     * 绑定键盘敲击事件 —— 用于按 回车键 发送消息     */    $(window).keydown(function (event) {        if (event.keyCode === 13) {            let message = $(".messageArea").val();            showServerMessage(message);        }    });    /**     * 绑定 webSocket,连接 服务器     */    webSocketBind();});websocket 通信测试


作者:蚩尤后裔
blog.csdn.net/wangmx1993328/article/details/84582904

精彩推荐
华为OD(外包)社招技术二面,总结复盘

冒着被开除的风险也要给大家看看看这份SpringCloud 总结

微服务 2.0 技术栈选型手册

天天在用Stream,那你知道如此强大的Stream的实现原理吗?


  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值