easy connect 获取服务端配置信息失败_技术小贴士——基于springboot实现websocket服务端及测试客户端...

news/2024/7/7 6:03:31

381a0f4cb046929d0e289736a8117589.png

技术小贴士—基于springboot实现websocket服务端及测试客户端

  • ffb095acb64514c0fd5c50042cf634c6.png
  • 目录

    1.webSocket简介

    2.使用场景

    3.工程简介

    3.服务端webSocketServer

       所需pom依赖

       开启webSocket所需要的配置支持

       webSocket服务端

       需要注意的问题及常见异常

    4.客户端webSocketClient

       所需pom依赖

       webSocket客户端

    需要注意的问题及常见异常

    5.继续了解webSocket

      在java中webSocket的5种状态

      connect()与reconnect()

      心跳机制及断线重连方法

  • d59569f5202321dd5753f9d13fe33cf9.png
d23c828f9c5a5f5519e92e34d53cb4ad.png 1.webSocket简介 WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。 在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。 简单的说,就是一次握手,持续通信。 2262d59038ebe9c5e3b3e0112a0b9da6.png a2c2ce8fd7ff2baea744ef8437b7f430.png 2.使用场景 采用java实现的websocket客户端与服务端除聊天室实现外,因其交互只需建立一次链接关系,极大的节省了内存与带宽,所以也常用于实时数据传输与获取。 某些业务需要在较短的时间间隔下,不断的去获取或传输数据,便可以考虑采用webSocket。 如:实时公交位置的获取,实时人员位置的获取,暴雨天气中水库的水位,某设备的实时温度等等。 1862e47c39e8e29175a8521be2c3d3a9.png 640?wx_fmt=gif 3.工程简介 本项目共分两个模块 (1.)websocket服务端,采用java语言实现,继承springboot框架,使用maven依赖 (2.)websocket测试用客户端,采用java语言实现,使用maven依赖 不必过分纠结项目中依赖所使用的版本,根据各自项目所需,切换合适的版本即可 (3.)服务端webSocketServer 首先我们来贴上关键代码,然后再进行解读
     以下代码共三部分: 所需pom依赖 开启webSocket所需要的配置支持 webSocket服务端 接下来我们依次来看: 所需pom依赖 eddd762eee1243bc5cf7805aa9f21ebd.png
 <dependency>      <groupId>org.springframework.bootgroupId>      <artifactId>spring-boot-starter-websocketartifactId> dependency> <dependency>      <groupId>com.alibabagroupId>      <artifactId>fastjsonartifactId>      <version>1.2.47version> dependency>
springboot集成了对webSocket的操作,此处我们使用的版本为2.3.3,同时涉及到数据通信,难免用到json解析,所以此处我们添加alibaba的fastjson依赖,用作解析json数据 开启webSocket所需要的配置支持
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter;/** * @author zhaiLiMing * @version 2020-9-16 * webSocket配置开启websocket支持 */@Configurationpublic class WebSocketConfig {    @Bean    public ServerEndpointExporter serverEndpointExporter(){        return new ServerEndpointExporter();    }}
4789b4a19e6b093164b63a2a129bbec6.png webSocket服务端 服务端基于5个注解实现,分别是: <1.@ServerEndpoint("/url") 该注解用于注释服务端的类,被该注解注释的类,将会被标注为webSocket的服务类,参数value为访问的路径 <2.@OnOpen 被该注解注释的方法,将在客户端与服务端建立连接时执行 <3.@OnMessage 被该注解注释的方法,将在服务端收到消息时执行 <4.@OnClose 被该注解注释的方法,将在链接关闭时执行 <5.@OnError 被该注解注释的方法,将在链接发生错误时执行
package com.modules.web;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONArray;import com.modules.service.StudentServiceImpl;import com.modules.utils.DataTranslate;import org.springframework.stereotype.Component;import javax.websocket.*;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.List;import java.util.Map;/** * @author zhaiLiMing * @version 2020-9-16 * webSocket服务端 * @ServerEndpoint 将本类注解为webSocket服务端,其value为客户端访问URI * @Compoent 使得客户端在spring容器启动时候就被加载 */@ServerEndpoint("/endpoint")@Componentpublic class WebSocketServer {    public WebSocketServer(){        System.out.println("EchoSocket:start");    }    private Session session;    /**     * 实例化service层,此处不能使用autowired等注解自动注入,     * 因spring的bean是默认单例模式     */    private static StudentServiceImpl studentService=new StudentServiceImpl();    /**     * 打开连接时执行     */    @OnOpen    public void onOpen(Session session) {        this.session = session;        System.out.println("连接已经打开");    }    /**     * 收到消息时执行     */    @OnMessage    public void onMessage(String message, Session session) {        System.out.println("从客户端收到的消息:" + message);         sendMessage(JSON.toJSONString(JSONArray.toJSONString(“返回给客户端的消息”)));    }    /**     * 关闭连接时执行     */    @OnClose    public void onClose(Session session) {        System.out.println("连接已经关闭");    }    /**     * 连接发生错误时执行     */    @OnError    public void onError(Throwable error, Session session) {        System.out.println("连接发生错误");    }    /**      websocket session发送文本消息有两个方法:getAsyncRemote()和getBasicRemote()      getAsyncRemote()和getBasicRemote()是异步与同步的区别,      大部分情况下,推荐使用getAsyncRemote()。    */    public void sendMessage(String message) throws IOException {        this.session.getAsyncRemote().sendText(message);    }}
需要注意的问题及常见异常 如果需要在webSocket服务类中调用service层,使用注解(如@Autowired等)自动注入,会抛出空指针异常。     此处原因是,websocket每接收到一个客户端的握手请求,就会开启一个新的线程来处理该客户端,然而,spring的bean默认是singleton单例模式,所以就会导致此类问题。     针对其的解决方法,可以采用传统的new方式去创建javaBean,或者修改spring的bean为prototype。
19ae88bd0ce5d5c6f90cb7c8d9286ef5.png 2e4b9e2f6725857d34a62d6471d92240.png

4.客户端webSocketClient

所需pom依赖

  <dependency>      <groupId>org.java-websocketgroupId>      <artifactId>Java-WebsocketartifactId>      <version>1.3.8version>  dependency>    <dependency>      <groupId>com.alibabagroupId>      <artifactId>fastjsonartifactId>      <version>1.2.47version>  dependency>
d23c828f9c5a5f5519e92e34d53cb4ad.png

针对所使用的依赖不再过多赘述

webSocket客户端

webSocket客户端的实现基于webSocketClient类实现,实例化webSocketClient并重写以下四个方法:

<1.onOpen 与服务端建立连接时执行

<2.onMessage 收到服务端消息时执行

<3.onClose 连接关闭时执行

<4.onError 发生错误时执行

a2c2ce8fd7ff2baea744ef8437b7f430.png
import com.alibaba.fastjson.JSON;import modules.entry.student.Student;import modules.service.StudentService;import modules.service.StudentServiceImpl;import modules.utils.JsonFormat;import org.java_websocket.WebSocket;import org.java_websocket.client.WebSocketClient;import org.java_websocket.handshake.ServerHandshake;import java.net.URI;import java.net.URISyntaxException;import java.util.List;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * @author zhaiLiMing * @version 2020-9-21 * webSocketClient客户端 */public class WebsocketClient {    //创建webSocketClient客户端    private static WebSocketClient client;    //实例化service层    private static StudentService studentService=new StudentServiceImpl();    //创建一个5个线程的线程池,用来接收onMessage    private static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);    public static void main(String[] args) throws URISyntaxException, InterruptedException {        //实例化webSocketClient,以ws或wss形式发送请求,重写4个方法        client=new WebSocketClient(new URI("ws://localhost:8080/endpoint")) {            //创建连接时执行            @Override            public void onOpen(ServerHandshake serverHandshake) {                System.out.println("建立连接");            }            //接收到消息时执行            @Override            public void onMessage(String s) {                Runnable runnable = new Runnable() {                    @Override                    public void run() {                        System.out.println(JSON.parseObject(s).getString("code"));                        //业务处理,接收返回的消息,解析JSON字符串,存入数据库                        if (JSON.parseObject(s).getString("code").equals("1100")){                            String data=JSON.parseObject(s).getString("data");                             //JsonFormat是自己写的工具类                            List studentList= JsonFormat.jsonFormatStudent(data);                            //调用service接收结果                            int result=studentService.insert(studentList);                            System.out.println("插入成功:"+result+"条数据!");                        }else{                            System.err.println("服务器出错!");                        }                    }                };                fixedThreadPool.submit(runnable);            }            //连接关闭时执行            @Override            public void onClose(int i, String s, boolean b) {                System.out.println("链接关闭");            }            //连接出错时执行            @Override            public void onError(Exception e) {                System.out.println("链接出错");            }        };        client.connect();        //检测连接状态,重复尝试连接        while (!client.getReadyState().equals(WebSocket.READYSTATE.OPEN)) {            System.out.println("before reconnect statte:"+client.getReadyState());            Thread.sleep(2000);            if(client.getReadyState().equals(WebSocket.READYSTATE.CLOSING) || client.getReadyState().equals(WebSocket.READYSTATE.CLOSED)){                client.reconnect();            }            System.out.println("After reconnect statte:"+client.getReadyState());        }        //发送数据        client.send("getStudent");    }}

需要注意的问题及常见异常

实例化webSocketClient时,有一个参数URI,URI中传入地址,有两种请求方式

<1.ws请求:其类似于http请求,非安全

<2.wss请求:其类似于https请求,安全

请求方式不同时,可能会抛出异常,两种请求方式具体区别在此不做解释,可查阅别的文章

b8943030da8d3f7ae4e97df3c74bc907.png db9c292c33760d9b89e40ae5fd390f6e.png 2e4b9e2f6725857d34a62d6471d92240.png

5.继续了解webSocket

在java中webSocket的5种状态

参阅过webSocket API文档的部分朋友或许会疑惑,为什么是5种呢?API文档写的4种呀!
实际上我们仔细看就会发现,java中(基于其他语言的websocket没有研究,所以只说java),webSocket的源码里定义了内部枚举类READYSTATE,其中包含以下5种状态

<1.NOT_YET_CONNECTED 尚未链接

<2.CONNECTING 链接中

<3.OPEN 链接已打开

<4.CLOSING 链接正在关闭

<5.CLOSED 链接已经关闭

public static enum READYSTATE {        NOT_YET_CONNECTED,        CONNECTING,        OPEN,        CLOSING,        CLOSED;        private READYSTATE() {        }    }
不难看出,5种状态表明着webSocket的整个生命周期,这对于我们在使用webSocket时解决一些问题是非常关键的 connect()与reconnect() 当webSocketClient初始化完毕之后,webSocketClient提供了两种链接方式,封装为两个方法,分别是 <1.connect() <2.reconnect() 那 么这两者有什么区别呢?这便涉及到了上一个问题,webSocket的5种状态。
起始时,webSocket状态为 NOT_YET_CONNECTED ,尚未链接,而当一次链接关闭之后,其状态为 CLOSED 。这两者虽然都是没有链接的状态,但本质上是有区别的。     NOT_YET_CONNECTED 表示该webSocket实例还未开始链接,并处于等待链接的状态,形象的讲,就是初生的婴儿;     而 CLOSED 则表示链接关闭,虽然也不是链接状态,但其表示已经完成了一次生命周期,该webSocket实例到了消亡的时候,形象的讲,就是垂暮的老人。    而webSocket想要链接,则只能在 NOT_YET_CONNECTED 状态下进行,一旦状态改变,则无法再次链接。这便是connect()链接。针对其解决方法,就是reconnect()链接。     reconnect()链接的实现,便是在connect之前调用了reset()方法,重置了当前webSocket,使得状态又改变成了 NOT_YET_CONNECTED ,从而可以再次执行connect()方法,我们看一下源码:
  public void reconnect() {        this.reset();        this.connect();    }

心跳机制及断线重连方法

了解了webSocket的5种状态以及connect()与reconnect()的区别后,就不难理解断线重连和心跳机制。

    所谓心跳机制,即为每隔一定时间,由客户端发送特定的心跳包给服务器,服务器也回应消息,双方互相确认对方还"活着"。     例如我们每隔10秒则调用 webSocket.send("心跳包")
同时在onMessage中接收到返回的内容,如果能接收到预期返回的内容,则证明双方都存在,反之则证明有一方挂掉。     至于重连机制,则可以利用reconnect()方法,在检测到断线后,重新尝试链接服务端
//开启一个新线程new Thread(){    @Override    public void run(){         try{         //间隔10秒发送心跳             Thread.sleep(10000);             webSocketClient.send("心跳包");         }catch (Exception e){         //捕获异常进行重连             webSocketClient.reconnect();         }     }   }.start();
c0060c951072ce46b5f22b1d9393cae4.png

好啦本期的小贴士就结束啦

最后送大家一句话,希望大家共勉

最好的节约是珍惜时间,最大的浪费是虚度年华。

我们下期再见

编辑:郭宇璐 李聪
终审:柴海明

b12467b5a458d0e043a0ae401b9b4f41.png


http://www.niftyadmin.cn/n/3661305.html

相关文章

[瞭望]从西溪湿地到中山古镇 --看“经营政府”

2006年09月13日 23:26:00 因为ZJ项目的培训需要&#xff0c;我离开泰安&#xff0c;奔赴大上海。或许是在泰安生活得久了&#xff0c;在上海的日日夜夜&#xff0c;我对它的快节奏深有感触&#xff1a;早晨起得要早&#xff0c;并且要随时带点可以阅读的资料&#xff0c;这样才…

暗黑2魔电西格玛攻略_【暗黑3资讯】暗黑攻略站:死灵法师无敌死疫尸矛详解...

欢迎收看本期的暗黑攻略站&#xff0c;本节目作为《暗黑3分钟》系列的延伸&#xff0c;将为各位因时长不足而无法展示到节目中的玩法介绍和机制剖析。本期我们的主角是死灵法师的无敌死疫尸矛&#xff0c;他造价低廉且特效炫酷&#xff0c;适合拿来速刷~视频1&#xff1a;暗黑3…

python数据清洗实例_2020 版 Python 数据清理终极指南!

作者 | Lianne & Justin 译者 | 陆离 一般来说&#xff0c;我们在拟合一个机器学习模型或是统计模型之前&#xff0c;总是要进行数据清理的工作。因为没有一个模型能用一些杂乱无章的数据来产生对项目有意义的结果。 数据清理或清除是指从一个记录集、表或是数据库中检测和…

[数据库]Oracle的培训讲师是个东北人

2006年12月25日 23:23:00 这是我这么多年来&#xff0c;听过的最痛快的技术培训&#xff0c;或许也是因为我在若干年前就对培训内容比较掌握的缘故吧&#xff0c;当我更感觉是因为Oracle的培训体系之精妙和讲师的妙语连珠&#xff0c;是我重新燃起了对Oracle的兴趣。体系之精妙…

b超可以看出什么_做B超,涂在肚子上黏糊糊的液体是什么?

说到B超检查&#xff0c;相信很多人都做过&#xff0c;尤其是孕妇&#xff0c;在孕期会做这项检查&#xff0c;了解肚子里宝宝的健康&#xff0c;对于普通人来说&#xff0c;也可以进行B超检查&#xff0c;充分判断脏腑器官好坏。作为最基本、最常见的检查项目&#xff0c;大家…

springboot controller里出现异常会导致服务器宕机_教你如何在Spring Boot中使用RSocket...

1. 概述RSocket应用层协议支持 Reactive Streams语义&#xff0c; 例如&#xff1a;用RSocket作为HTTP的一种替代方案。在本教程中&#xff0c; 我们将看到RSocket用在spring boot中&#xff0c;特别是spring boot 如何帮助抽象出更低级别的RSocket API。2. 依赖让我们从添加sp…

带网格的_最美网格员丨船只撞坏防洪墙就跑,有他在,妥妥的解决问题!

你好&#xff0c;我们是&#xff1a;网格员&#xff01;网格是基层社会治理最小的单元&#xff0c;是社会治理精细化、精准化的重要载体。有一群人&#xff0c;上管天&#xff0c;下管地&#xff0c;中间管空气&#xff0c;他们每天奔波在大街小巷、田间地头&#xff0c;不惧重…

[啃书]身体使用手册 --《You:The owner's manual》

2006年12月16日 21:48:00 去济南会老铁&#xff0c;顺便去泉城路书店逛逛&#xff0c;给豆豆也给自己相本书。挑来挑去&#xff0c;选了本据说是欧美第一健康书&#xff0c;钟南山院士作序的&#xff0c;中文翻译成《You:身体使用手册》。在回泰安的路上&#xff0c;迫不及待的…