Tomcat Websocket应用实例研究

概述

本文介绍了如何根据Tomcat给出的websocket实例,通过对实例的学习,定制自己基于websocket的应用。

环境及版本:

  1. Ubuntu 22.04.4 LTS
  2. Apache Tomcat/10.1.20
  3. openjdk 11.0.23 2024-04-16
  4. 浏览器:Chrome

相关资源及链接

Class Loader How-To:

Apache Tomcat 11 (11.0.0-M20) - Class Loader How-To

推荐几篇本站内介绍Websocket原理及tomcat附带的实例的文章,可作为参考:

Websocket原理-CSDN博客

看完让你彻底理解 WebSocket 原理_websocket原理-CSDN博客

Tomcat实现Web Socket_tomcat 9 wss服务配置-CSDN博客

Tomcat提供的websocket实例

Tomcat安装完成后给出的Examples中,包括了有关websocket的例子。

如上图,点击‘Examples’,进入如下界面:

继续点击‘WebSocket Examples’,进入如下界面:

点击‘Echo example’,进入如下界面:

从界面可以看出,Tomcat提供以下三种方式与服务器建立websocket双向通信:

  1. programmatic API
  2. annotation API (basic)
  3. annotation API (stream)

programmatic:编程式,即编写一个Java类继承javax.websocket.Endpoint(根据tomcat及openjdk的版本不同,或继承jakarta.websocket.Endpoint,本文中为jakarta),并实现它的onOpen、onClose和onError等方法。

annotation:注解式,实现一个业务类并给其添加websocket相关的注解(通过@ServerEndpoint(...)),注解表明当前业务类是已经实现了WebSocket规范的Endpoint。根据上面tomcat给出的实例界面显示,注解式又分为basic和stream两种模式。

本文不对上述三种方式展开详细讨论。

点击上面(tomcat)界面的三种websocket的实现方式,下方的编辑框中会同步显示将实际在代码中用到的websocket URL,例如点击‘annotation API (basic)’,下面编辑框的内容同步更新为‘ws://host/examples/websocket/echoAnnotation’,其中host为服务器的URL(含端口),以下均使用‘127.0.0.1:8080’作为默认值,例如:

ws://127.0.0.1:8080/examples/websocket/echoAnnotation

客户端浏览器将使用该URL串作为目标websocket服务器地址。

点击‘Connect’按钮,再点击‘Echo message’按钮,界面如下:

依葫芦画瓢

now,我们照着tomcat给出的实例依葫芦画瓢建立自己的websocket应用,并试图在这一过程中逐步理解tomcat的websocket实现原理以及相关的配置。

新建一个自己的webapp,例如命名为myws:

  1. 在目录‘opt/tomcat/webapps’新建目录‘myws’;
  2. 将examples实例下‘websocket’目录及其文件拷贝到‘myws’目录下;
  3. 在‘myws’目录下新建目录‘WEB-INF’;进入新建的‘WEB-INF’目录,继续创建目录‘classes’,此目录为本文涉及的tomcat 11加载Java类的默认目录!
  4. 将examples实例下的‘WEB-INF/classes/websocket’目录及其文件拷贝到myws应用下新建的‘classes’目录。

依葫芦画瓢(文件拷贝)暂时到此。

在新建的myws应用根目录下新建一个index.html文件,内容如下:

<html lang="zh-CN">

<head>

<meta charset="UTF-8">

<title>项目测试首页列表</title>

<style>

  body {

    background-color: lightblue;

  }

  h1 {

    text-align: center;

  }

  div.exam_list {

    font-family: verdana;

    font-size: 18px;

    margin-left: 100px;

    margin-top:5px;

  }

</style>

</head>

<body>

<h1>tomcat websocket应用学习</h1>

<p>

<div class="exam_list"><li><a href="/myws/websocket/index.xhtml">examples实例学习</a></li></div>

</p>

</body>

</html>

启动浏览器,输入URL,例如:http://hostname/myws,出现如下界面:

点击页面链接,进入如下界面(此界面与之前的完全相同):

点击‘Echo example’,进入如下界面(此界面与之前的完全相同):

分别点击tomcat下websocket的三种通信实现方式,下方编辑框的链接URI为:

  1. ws://127.0.0.1:8080/examples/websocket/echoProgrammatic
  2. ws://127.0.0.1:8080/examples/websocket/echoAnnotation
  3. ws://127.0.0.1:8080/examples/websocket/echoStreamAnnotation

点击页面其他按钮,并操作,一切正常,注意观察三个URL,其仍然连接的是安装包默认提供的examples项目上了,故一切正常

打开并编辑文件‘echo.xhtml’,注意如下代码行:

<div>

    <div id="connect-container">

        <div>

            <span>Connect to service implemented using:</span>

            <br/>

            <!-- echo example using new programmatic API on the server side -->

            <input id="radio1" type="radio" name="group1" value="/examples/websocket/echoProgrammatic"

                   onclick="updateTarget(this.value);"/> <label for="radio1">programmatic API</label>

            <br/>

            <!-- echo example using new annotation API on the server side -->

            <input id="radio2" type="radio" name="group1" value="/examples/websocket/echoAnnotation"

                   onclick="updateTarget(this.value);"/> <label for="radio2">annotation API (basic)</label>

            <br/>

            <!-- echo example using new annotation API on the server side -->

            <input id="radio3" type="radio" name="group1" value="/examples/websocket/echoStreamAnnotation"

                   onclick="updateTarget(this.value);"/> <label for="radio3">annotation API (stream)</label>

            <br/>

            <!-- echo example using new annotation API on the server side -->

            <!-- Disabled by default -->

            <!--

            <input id="radio4" type="radio" name="group1" value="/examples/websocket/echoAsyncAnnotation"

                   οnclick="updateTarget(this.value);"/> <label for="radio4">annotation API (async)</label>

            -->

        </div>

根据代码,如前所述,每当用户点击了不同的通信方式,页面会自动更新websocket连接,其中实例代码还注释掉了第四种方式‘AsynAnntation’。

将上述代码中高亮的‘examples’替换为本项目名称‘myws’。刷新页面,点击选择不同的通信方式,确认编辑框中websocket连接URL更新。

回到页面进行操作,OK!一切正常!!!

温馨提示(重要的问题说三遍),在测试页面之前务必通过tomcat的管理页面重新启动web应用,界面如下:

点击‘停止’按钮,再点击‘启动’按钮。

不知道如何配置管理页面的,可直接重启tomcat服务。

重要的事情说三遍!!!一定记得重启应用!

tomcat三种websocket通信方式测试

一个小测试:

查看目录‘myws/WEB-INF/classes/websocket’,除了四个子目录,注意该目录下有两个文件,一个是‘ExamplesConfig.java’,另一个是对应的class文件。

从项目目录中删除该两个文件,重新启动web应用,再次进入Echo example界面,同样进行三种方式的通信测试,其中后两种(基础注解式/annotation API (basic)和流式注解式/annotation API (stream))正常,第一种‘编程式’连接失败,连接失败界面如下。

查看目录‘myws/WEB-INF/classes/websocket/echo’,该目录下文件列表如下图。

再次回顾前文提到的三种通信方式的URL,如下:

  1. ws://127.0.0.1:8080/examples/websocket/echoProgrammatic
  2. ws://127.0.0.1:8080/examples/websocket/echoAnnotation
  3. ws://127.0.0.1:8080/examples/websocket/echoStreamAnnotation

查看文件EchoAnnotation.java,在类定义之前有一处申明,代码如下:

注解式下,通过‘@ServerEndpoint’添加注解后,在项目(网站)启动时tomcat服务会自动扫描(WEB-INF/calsses目录下)java类,并将注解类与ws服务关联。

查看文件EchoStreamAnnotation.java,同样有一处类似的申明,如下:

在EchoEndpoint.java文件中,没有发现类似的注解。

所以,在删除了文件ExampleConfig(并重新启动应用)后,注解式的方式依然有效,编程式的方式连接失败。

重新拷贝ExampleConfig.java/.class到应用目录,查看ExampleConfig.java,内容如下:

package websocket;

import java.util.HashSet;

import java.util.Set;

import jakarta.websocket.Endpoint;

import jakarta.websocket.server.ServerApplicationConfig;

import jakarta.websocket.server.ServerEndpointConfig;

import websocket.drawboard.DrawboardEndpoint;

import websocket.echo.EchoEndpoint;

public class ExamplesConfig implements ServerApplicationConfig {

    @Override

    public Set<ServerEndpointConfig> getEndpointConfigs(

            Set<Class<? extends Endpoint>> scanned) {

        Set<ServerEndpointConfig> result = new HashSet<>();

        if (scanned.contains(EchoEndpoint.class)) {

            result.add(ServerEndpointConfig.Builder.create(

                    EchoEndpoint.class,

                    "/websocket/echoProgrammatic").build());

        }

        if (scanned.contains(DrawboardEndpoint.class)) {

            result.add(ServerEndpointConfig.Builder.create(

                    DrawboardEndpoint.class,

                    "/websocket/drawboard").build());

        }

        return result;

    }

    @Override

    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {

        // Deploy all WebSocket endpoints defined by annotations in the examples

        // web application. Filter out all others to avoid issues when running

        // tests on Gump

        Set<Class<?>> results = new HashSet<>();

        for (Class<?> clazz : scanned) {

            if (clazz.getPackage().getName().startsWith("websocket.")) {

                results.add(clazz);

            }

        }

        return results;

    }

}

首先类ExampleConfig继承自ServerApplicationConfig,该类会执行目录自动扫描,对于目录(及子目录)下所有继承自‘Endpoint’的类进行处理,并分别映射了两个ws服务:

  1. "/websocket/echoProgrammatic"
  2. "/websocket/drawboard"

其中第一个正是echo测试中的第一种基于编程式的通信方式所对应的服务,第二个为多人协同画板应用的实例服务(对应的注册名称)。

对于所有注解式实现的websocket服务(类),示例代码中进行了过滤操作,即任何不是以‘websocket.’开头的服务,都将被屏蔽。上面的实际测试中(删除ExamplesConfig),注解式的不需要代码中的add(clazz)操作也可以正常工作。

另外,在自己的应用中,可将配置文件/类(ExamplesConfig)更改为项目对应的名称,例如本例中更改为MywsConfig.java/class,记得类名与文件一致,重新编译.java,并重启web应用。

记录一下ubuntu下成功编译MywsConfig.java的命令(好记性不如烂笔头),主要是指定import的相关库/类的路径,如下:

javac -cp /opt/tomcat/webapps/myws/WEB-INF/classes:/opt/tomcat/lib/* MywsConfig.java

其他

Tomcat有关websocket实现的包在目录$CATALINA_HOME/lib($CATALINA_HOME的默认安装目录为‘/opt/tomcat’)下,包含三个文件,如下图:

本文未涉及注解式以及编程式websocket通信的各接口的分析,相关文章可在站内搜索。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/29515.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python连接数据库,相关数据处理

随机生成一千个数据插入large_db中 # 这是一个示例 Python 脚本。# 按 ShiftF10 执行或将其替换为您的代码。 # 按 双击 Shift 在所有地方搜索类、文件、工具窗口、操作和设置。 import pandas as pd from sqlalchemy import create_engine from faker import Faker# 初始化fa…

【价值主张画布】以产品思维,将自己打造成“爆款”

经营自己等于经营公司&#xff1a; 1.客户细分&#xff1a;我能帮助谁&#xff1f;谁是我们最重要的客户&#xff1f; 2. 客户关系&#xff1a;怎样和对方打交道&#xff1f;一次交付还是持续交付&#xff1f; 3.渠道通路&#xff1a;怎样宣传自己和服务&#xff1f; 4. 价值主…

Jmeter 性能测试步骤是什么?

性能测试是软件开发过程中非常重要的一环。它可以帮助我们评估软件系统在不同负载下的性能表现&#xff0c;找出系统中的性能瓶颈&#xff0c;并提供改进方案。而JMeter作为一款功能强大且广泛使用的性能测试工具&#xff0c;可以帮助我们实现这一目标。 下面&#xff0c;我将…

银河麒麟4.0.2安装带有opengl的Qt5.12.9

银河麒麟4.0.2下载地址&#xff1a;银河麒麟-银河麒麟(云桌面系统)-银河麒麟最新版下载v4.0.2-92下载站 VirtualBox:https://www.virtualbox.org/wiki/Downloads qt下载&#xff1a;Index of /archive/qt/5.12/5.12.9 1安装VirtualBox:网上教材比较多 1&#xff09;安装完后安…

干货 | 使用 Navicat BI 解锁数据的力量

商业智能&#xff08;BI&#xff09;是一种将数据转化为可执行洞察的实践&#xff0c;能够帮助业务领导者提升整体业绩。这个过程中最重要的一个阶段是数据探索和可视化阶段&#xff0c;它涉及通过报告将数据组织并转化为有意义的信息。为了让数据更易于理解&#xff0c;BI 专业…

DashText-进阶使用

前置知识 BM25简介 BM25算法&#xff08;Best Matching 25&#xff09;是一种广泛用于信息检索领域的排名函数&#xff0c;用于在给定查询&#xff08;Query&#xff09;时对一组文档&#xff08;Document&#xff09;进行评分和排序。BM25在计算Query和Document之间的相似度…

四川赤橙宏海商务信息咨询有限公司正规吗?

在数字化浪潮席卷全球的今天&#xff0c;电商行业正以前所未有的速度蓬勃发展。作为这一潮流的佼佼者&#xff0c;抖音电商以其独特的短视频直播模式&#xff0c;吸引了大量消费者和商家的目光。在这一背景下&#xff0c;四川赤橙宏海商务信息咨询有限公司应运而生&#xff0c;…

2Y0A21 GP2Y0A21YK0F 红外测距传感器 arduino使用教程

三根线 正极负极信号线 确认自己的三根线分别代表什么&#xff0c;我们的颜色可能不一样 附一张我买的传感器他们的说明图 正极 接 开发板5V 负极 接开发板GND 信号线 接A0 代码arduino ide上面写 // Infračerven senzor vzdlenosti Sharp 2Y0A21// připojen potře…

2024最新AI大模型-LLm八股合集(三)

常见的大模型 1.ChatGLM 1.1 背景 主流的预训练框架主要有三种&#xff1a; autoregressive自回归模型&#xff08;AR模型&#xff09; &#xff1a;代表作GPT。本质上是一个left-to-right的语言模型。 通常用于生成式任务 &#xff0c;在长文本生成方面取得了巨大的成功…

【x264】整体框架汇总

【x264】整体框架汇总 1. x264整体框架图2. 思考 参考&#xff1a; x264源代码简单分析&#xff1a;概述 参数分析&#xff1a; 【x264】x264编码器参数配置 流程分析&#xff1a; 【x264】x264编码主流程简单分析 【x264】编码核心函数&#xff08;x264_encoder_encode&…

空气流量和空气压力参数解耦系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 空气流量和空气压力参数解耦系统simulink建模与仿真&#xff0c;在许多系统中&#xff0c;空气流量&#xff08;Q&#xff09;和压力&#xff08;P&#xff09;之间存在耦合关…

SpringBoot + thymeleaf 修改文件,刷新页面不能实时展示修改后的内容问题解决

修改页面后文件后&#xff0c;刷新页面&#xff0c;内容不变&#xff0c;是因为项目没有编译&#xff0c;没有将新的页面文件编译&#xff0c;以下方法可以完美解决次问题 具体内容请查看&#xff1a;http://www.haozgx.top/blog/article/2

vue element-ui 车牌号选择组件

实现效果 carnumber.vue <template><div class"car_no_popover"><div class"row_div"><div class"every_div" v-for"item in area"><button click"selectCarNo(item)">{{ item }}</butto…

绿色免费离线版JS加密混淆工具 - 支持全景VR加密, 小程序js加密, H5网站加密

自从我们推出在线版的免费JS加密混淆工具以来&#xff0c;受到了广大用户的热烈欢迎。特别是全景开发人员&#xff0c;他们使用该工具加密VR插件的JS代码, 添加域名锁等&#xff0c;都非常有效地保护了插件的代码资源。 最近&#xff0c;我们收到了许多用户的反馈&#xff0c;…

Cloneable接口和对象的克隆——浅拷贝和深拷贝

用Clonable接口实现对象的克隆——浅拷贝和深拷贝 1. 浅拷贝2. 深拷贝 在Object类中提供了clone方法&#xff0c;用来是实现对象的克隆&#xff01; 1. 浅拷贝 我们首先来尝试用clone方法去克隆一个Person对象 public class Person {public String name;public int age;publi…

Perforce静态代码分析专家解读MISRA C++:2023®新标准:如何安全、高效地使用基于范围的for循环,防范未定义行为

MISRA C&#xff1a;2023——MISRA C 标准的下一个版本来了&#xff01;为了帮助您了解 MISRA C&#xff1a;2023相比于之前版本的变化&#xff0c;我们将继续为您带来Perforce首席技术支持工程师Frank van den Beuken博士的博客系列&#xff0c;本期为第三篇。 在前两篇系列文…

企业为何需要搭建线上虚拟品牌展厅?

在数字化时代&#xff0c;线上虚拟品牌展厅已成为企业不可或缺的一部分。以下是构建线上虚拟品牌展厅的4大关键理由&#xff1a; 1、迎合在线购物趋势 随着移动互联网的飞速发展和普及&#xff0c;消费者越来越倾向于在线购物。一个线上虚拟品牌展厅能够完美地满足这一需求&am…

数据分析必备:一步步教你如何用matplotlib做数据可视化(5)

1、Matplotlib Multiplots 在本章中&#xff0c;我们将学习如何在同一画布上创建多个子图。 subplot()函数返回给定网格位置的axes对象。此函数的签名是 plt.subplot(subplot(nrows, ncols, index)在当前图中&#xff0c;该函数创建并返回一个Axes对象&#xff0c;在ncolsaxe…

VScode如何调试

调试 1.打断点 1.点击调试按钮 3.点击下拉选择环境node&#xff0c;点击绿三角选择输入调试的命令&#xff08;具体命令查看package.json中scripts中的哪一个命令和运行的文件&#xff09;&#xff0c;点击右边的设置&#xff08;可以直接跳下面第八步&#xff01;&#xff…

EOS Black灵魂回响黑色账号注册 EOS Black怎么注册账号教程

又一款新的MMORPG游戏即将上线&#xff0c;游戏名称叫做《灵魂回响&#xff1a;黑色》&#xff0c;游戏继承了《灵魂回响》系列的基本世界观和背景故事&#xff0c;从危险中救出来的阿尔卡纳们沉醉于权力开始堕落, 少数阿尔卡纳还没有忘记自己的本分,为净化世界而努力&#xff…