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

程序员的成长之路
互联网/程序员/技术/资料共享 
关注


阅读本文大概须要 6.5 分钟。javascript

做者:蚩尤后裔
来源:https://blog.csdn.net/wangmx1993328/article/details/84582904

# 传统 Tomcat 开发 WebSocket 回顾

WebSocket 的出现是基于 Web 应用的实时性须要而产生的,在淘宝、京东等网页客服、网页卖家聊天等需求上应用普遍。对于前端网页可使用 H5 开发 WebSocket 客户端,也可使用 SockJS 库开发 WebSocket 客户端。

对于Java 开发者而言,后台 WebSocket 服务端开发一般有如下经常使用的选择:
  • Tomcat7 之后开始支持 websocket 协议css

  • Spring4 之后开始支持 WebSockethtml

  • 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 服务终端

一、建立后台 webSocket 服务端类,标识 @ServerEndpoint( javax.websocket.server.ServerEndpoint)注解,表示当前类是 webSocket 服务终端,同时在里面实现客户端链接创建、发送消息、接收消息等通讯业务。

二、这与传统导包式开发 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

一、注入 org.springframework.web.socket.server.standard.ServerEndpointExporter,这个 bean 会自动注册使用了@ServerEndpoint 注解声明的 Websocket endpoint 。

二、若是使用独立的 servlet 容器,而不是使用 spring boot 的内置容器,就不要注入ServerEndpointExporter,由于它将由 Tomcat 容器本身提供和管理。

三、由于传统导包式 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 通讯测试

<END>

推荐阅读:
java

知乎高赞:为何像王者荣耀这样的游戏Server不肯意使用微服务?
程序员

ELK太笨重了?想放弃?快试试日志系统新贵Loki吧!
web

5T技术资源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,单片机,树莓派,等等。在公众号内回复「2048」,便可免费获取!!

微信扫描二维码,关注个人公众号spring

写留言

朕已阅 api

本文分享自微信公众号 - 程序员的成长之路(cxydczzl)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。浏览器

相关文章
相关标签/搜索