WebSocket四种事件详解 入门篇(二)

SpringBoot 使用WebSocket打造在线聊天室(基于注解) 推荐WebSocket的三大理由: 1、采用全双工通信,摆脱传统HTTP轮询的窘境。2、采用W3C国际标准,完美支持HTML5。3、简单高效,容易上手。 学习目标 快速学会通过WebSocket编写简单聊天功能。 快速查阅 专题阅读:《SpringBoot 布道系列》 温馨提示:1、WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速... 阅读详情
AI摘要

本文详细介绍了WebSocket的四个核心事件——打开、消息、错误和关闭,并通过Spring Boot提供了具体的应用案例。

 

 

示例代码  在文章最下方,可以根据示例代码和讲解来理解整个WebSocket流程。代码用SpringBoot 和 maven搭建

 

服务端和客户端四种事件

1、打开事件

    此事件发生在端点上建立新连接时并且在任何其他时间发生之前。

    方法级注解:@OnOpen

                        使用注解的的方法是没有任何返回值的公有方法,这些方法有一个可选的Session参数,一个可选的EndpointConfig参数,以及

                        任意数量的被@PathParam注解的String参数(这些参数的顺序是可以任意排列的)。

 

在案例中运用:

@OnOpen
public void whenOpening(Session session) {
    this.session = session;
    session.getUserProperties().put(START_TIME, System.currentTimeMillis());
    this.sendMessage("3:Just opened");
}

        

2、消息事件

    此事件接收WebSocket对话中另一端发送的消息。它发生在WebSocket端点接收了打开事件之后并且在接收关闭事件关闭连接之前的任意时刻。

    方法级注解:@OnMessage

                        连接上的消息以3中基本形式抵达:文本消息、二进制消息、或pong消息。

                        方法可以有返回参数,当使用方法有返回参数时,WebSocket实现立即将返回值作为消息返回给刚刚在方法中处理的消息发送者。

在案例中运用

一、

//只接受文本消息,不处理其他格式消息

@OnMessage
public void whenGettingAMessage(String message) {
    System.out.println("接收消息:"+message);
    if (message.indexOf("xxx") != -1) {
        throw new IllegalArgumentException("xxx not allowed !");
    } else if (message.indexOf("close") != -1) {
        try {
            this.sendMessage("1:Server closing after " + this.getConnectionSeconds() + " s");
            session.close();
        } catch (IOException ioe) {
            System.out.println("Error closing session " + ioe.getMessage());
        }
        return;
    }
    this.sendMessage("3:Just processed a message");
}
二、

 

//只接受二进制消息,不处理其他格式消息

@OnMessage
public void processBinary(byte[] messageDate,Session session) {
    //代码逻辑
}       

3、错误事件

    此事件在WebSocket连接或者端点发生错误时产生。

    方法级注解:@OnError

                        有三种基本类型的错误:

                        1、WebSocket实现产生的错误可能会发生。

                        2、错误可能发生在当WebSocket实现试图将入站消息解码成开发人员所要求的某个对象时。

                        3、最后由WebSocket端点的其他方法产生的运行错误。

在案例中运用

注意:第二种错误实际上调用的也是第一种注解方式

一、模拟异常错误
@OnError
public void whenSomethingGoesWrong(Throwable t) {
    this.sendMessage("2:Error: " + t.getMessage());
}
二、模拟解析消息时自定义错误

 

4、关闭事件

    此事件表示WebSocket端点的连接目前正在部分地关闭,它可以由参与连接的任意一个端点发出。

    方法级注解:@OnClose

                        生命周期的最后一个事件

在案例中运用

一、模拟客户端关闭WebSocket

@OnClose
public void whenClosing() {
    System.out.println("Goodbye !");
}
二、模拟客户端发送关闭请求,服务端关闭WebSocket

 

注意:其实方法二,最终调用过的也是方法一。所以当服务端或者客户端关闭WebSocket都是调用@OnClose方法。

 

案例运行:

1、开始界面

2、点击连接WebSocket端点(连接后可以进行操作)

 

3、发送消息(调用了@OnMessage 方法)

4、发送错误消息(会导致连接断开,先调用了@OncClose注解方法,判断消息不和规范抛出异常调用@OnError注解方法,之后调用@Onclose注解方法)

4、调用服务端关闭和调用客户端关闭操作(本质上调用的都是@Onclose注解方法)

实际上流程

服务端关闭:

    1)先调用了@OnMessage注解方法,

    2)判断入参为Close ,标识需要服务端关闭连接,服务端调用当前Session.Close()

    3)然后调用@OnClose注解方法关闭WebSocket连接

客户端关闭

    1)调用@OnClose注解方法关闭WebSocket连接

总结:

WebSocket声明周期

开始:通过@OnOpen开始

通讯:通过@OnMessage进行通讯

中断:@OnError  和 @OnClose 都会导致中断。

 

SpringBoot启动配置(配置完成直接运行此类就可以)

package Develop;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {
    /*
        @SpringBootApplication
       标注启动配置入口,可以发现通过一个main方法启动。
       使用这个注解的类必须放置于最外层包中,因为默认扫描这个类以下的包。
       否则需要自己配置@ComponentScan。
     */
    public static void main(String[] arg){
        SpringApplication.run(Application.class);
    }
}

 

扫描服务端端点配置(如没有,则服务端端点无法实例化成功)

package Develop;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

 

服务端端点

package Develop.Lifecycle;
import org.springframework.stereotype.Component;

import java.io.IOException;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/lights")
@Component
public class LifecycleEndpoint {
    private static String START_TIME = "Start Time";
    private Session session;

    @OnOpen
    public void whenOpening(Session session) {
        this.session = session;
        session.getUserProperties().put(START_TIME, System.currentTimeMillis());
        this.sendMessage("3:Just opened");
    }

    @OnMessage
    public void whenGettingAMessage(String message) {
        System.out.println("接收消息:"+message);
        if (message.indexOf("xxx") != -1) {
            throw new IllegalArgumentException("xxx not allowed !");
        } else if (message.indexOf("close") != -1) {
            try {
                this.sendMessage("1:Server closing after " + this.getConnectionSeconds() + " s");
                session.close();
            } catch (IOException ioe) {
                System.out.println("Error closing session " + ioe.getMessage());
            }
            return;
        }
        this.sendMessage("3:Just processed a message");
    }

    @OnError
    public void whenSomethingGoesWrong(Throwable t) {
        this.sendMessage("2:Error: " + t.getMessage());
    }

    @OnClose
    public void whenClosing() {
        System.out.println("Goodbye !");
    }

    void sendMessage(String message) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (Throwable ioe) {
            System.out.println("Error sending message " + ioe.getMessage());
        }
    }

    int getConnectionSeconds() {
        long millis = System.currentTimeMillis() -
                ((Long) this.session.getUserProperties().get(START_TIME));
        return (int) millis / 1000;
    }
}

 客户端页面(可以自行更改服务端连接地址)


<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Lifecycle</title>
    </head>
    <body>
        <h1 style="text-align: center;">Lifecycle Lights</h1>

        <table style="text-align: left; width: 50px; margin-left: auto;
        margin-right: auto;" border="0" cellpadding="20" cellspacing="0">
            <tbody>
            <tr>
                <td style=" text-align: center; vertical-align: top;">
                    <div style="text-align: center;">
                    <form action="">
                        <input onclick="open_connection()" value="Open Connection" type="button" id="ocID">
                        <input onclick="send_valid_message()" value="Send Message" type="button" id="svmID">
                        <input onclick="send_invalid_message()" value="Send Bad Message" type="button" id="simID">
                        <input onclick="request_close_connection()" value="Server Close Connection" type="button"  id="rccID">
                        <input onclick="close_connection()" value="Client Close Connection" type="button" id="rcID">
                    </form>
                </div>
                </td>
            </tr>    
            <tr>
                <td style=" text-align: center; vertical-align: top;">
                    <canvas id="myDrawing" width="200" height="210"></canvas>
                    <div id="traffic_light_display"></div>
                </td>
            </tr>
            </tbody>
        </table>
        <div id="output"></div>
    </body>
    <script language="javascript" type="text/javascript">
            var lifecycle_websocket;
            
            function init() {
                output = document.getElementById("output");
                traffic_light_display = document.getElementById("traffic_light_display");
                update_display("1", "No connection");
                update_buttons();
            }

            function open_connection() {
                lifecycle_websocket = new WebSocket("ws://localhost:8080/lights");
                lifecycle_websocket.onmessage = function (evt) {
                    update_for_message(evt.data);
                    update_buttons();
                };
                lifecycle_websocket.onclose = function (evt) {
                    update_buttons();
                }; 
            }
            
            function get_color(light_index, light_on_index) {
                if (light_index == 1 && light_on_index == 1) {
                    return "red"
                } else if (light_index == 2 && light_on_index == 2) {
                    return "yellow"
                } else if (light_index == 3 && light_on_index == 3) {
                    return "green"
                } else {
                    return "grey"
                }  
            }
            
            function get_light_index(message) {
                return message.substring(0, 1)
            }
            
            function get_display_message(message) {
                return message.substring(2, message.length)
            }
            
            function update_for_message(message) {
                var display_message = get_display_message(message);
                var light_index = get_light_index(message);
                update_display(light_index, display_message);
            }
            
            function update_display(light_index, display_message) {
                var old = traffic_light_display.firstChild;
                var pre = document.createElement("pre");
                pre.style.wordWrap = "break-word"; 
                pre.innerHTML = "<b><font face='Arial'>"+display_message+"</font></b>";
                if (traffic_light_display.firstChild != null) {
                    traffic_light_display.replaceChild(pre, traffic_light_display.firstChild);
                } else {
                    traffic_light_display.appendChild(pre)
                }
                
                var context = document.getElementById('myDrawing').getContext('2d');
                context.beginPath();
                context.fillStyle = "black"
                context.fillRect(65,0,70,210);
                context.fill();
                
                context.beginPath();
                context.fillStyle = get_color(1, light_index); // grey
                context.arc(100,35,25,0,(2*Math.PI), false)
                context.fill();
                
                context.beginPath();
                context.fillStyle = get_color(2, light_index);
                context.arc(100,105,25,0,(2*Math.PI), false)
                context.fill();
                
                context.beginPath();
                context.fillStyle = get_color(3, light_index);
                context.arc(100,175,25,0,(2*Math.PI), false)
                context.fill(); 
            }
            
            function isOpen() {
                return lifecycle_websocket != null && lifecycle_websocket.readyState == lifecycle_websocket.OPEN;
            }
            
            function update_buttons() {
                ocID.disabled = isOpen();
                simID.disabled = !isOpen();
                svmID.disabled = !isOpen();
                rccID.disabled = !isOpen();
                rcID.disabled = !isOpen();
            }
            
            function send_valid_message() {
                lifecycle_websocket.send("Hello")  
            }
            
            function send_invalid_message() {
                lifecycle_websocket.send("Helxxxlo")
                
            }
            
            function request_close_connection() {
                lifecycle_websocket.send("close");
            }
            
            function close_connection() {
                lifecycle_websocket.close();
                update_display("1", "Client closed connection");
            }

            window.addEventListener("load", init, false);
        </script>
</html>

 

maven依赖



    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.3.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-juli</artifactId>
            <version>8.0.53</version>
        </dependency>

    </dependencies>

 

代码结构

 

 

Websocket相关问题总结(Session共享,用户多端登录等) 我们在使用websocket的时候其实主要面对的问题就是session共享的问题: 不管是基于Spring实现的Websocket的WebsocketSession 还是基于JDK实现的Session 亦或者基于netty实现的ChannelHandlerContext 用图来描述下场景吧: OK,大家看到这个图了,差不多应该明白了Session共享应该怎么处理了。其实原理很简单: ... 阅读详情

相关推荐

WebSocket详解

WebSocket详解:5分钟从入门到精通!!!

dfc_dfc的博客 6770

Spring Boot 中的 WebSocketSession 是什么,原理,如何使用

WebSocketSession 是 Spring Boot 中用于与客户端进行 WebSocket 通信的核心概念。在使用 WebSocketSession 时,我们需要先添加 Spring Boot 的 WebSocket 依赖,然后配置 WebSocket,并实现一个 WebSocket 处理器,最后在处理器中使用 WebSocketSession 进行通信。客户端也可以使用 WebSocketSession 进行通信,非常简单。

程序猿徐师兄的博客 9079

WebSocket系列之基础知识入门篇

概述 本文是WebSocket系列的第一篇,主要介绍WebSocket相关的基础协议知识和API。由于WebSocket的相关介绍在MDN中分布较乱,初学者不太容易入门,因此通过本文将相关基础知识和使用方法进行一个归纳和总结。 本文主要内容如下: WebSocket基础概念介绍 WebSocket协议初读 WebSocket 相关API浅析 WebSocket在线上项目中的使用 通过本文,你能够了解到WebSocket相关基础知识,同时了解到WebSocket在线

weixin_42899690的博客 622

WebSocket使用@OnMessage注解无法收到消息的坑与解决方法

一、前言 最近新写了一个Java WebSocket后台,用来和前端通信;本来前端通过js是可以成功建立WebSocket连接的,但是前端使用WebSocket发给后台的消息,后台却怎么也收不到。 以下是问题描述与解决办法。 二、问题描述 1.前端建立WebSocket连接的关键代码样例如下: var msg="abc123"; //var imUrl="wss://10.123.123.123:9999/api/im"; var imUrl="ws://10.123.123.123:9999/api/im

BHSZZY的博客 2万+

eclipse和tomcat8的websocket入门篇

最近笔者在做websocket的调研,本来以为很快就可以写一个demo出来,没想到一拖再拖,这么久了才写出一个demo。在网上找了很多例子,但是总觉得没说清楚。有的例子还很老了,是关于tomcat7的,但是tomcat7关于websocket的有些包已经启用,无法使之编译通过。还有的例子是关于jetty之类的其他服务器,由于tomcat使用比较广泛,所以在此笔者只调研了tomcat8的demo。我

strong815的专栏 5356

vue使用websocket

websocket其他特点如下: (1)建立在 TCP 协议之上,服务器端的实现比较容易。 (2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。 (3)数据格式比较轻量,性能开销小,通信高效。 (4)可以发送文本,也可以发送二进制数据。 (5)没有同源限制,客户端可以与任意服务器通信...

高山景行,一尘不染 1136

【Html5 WebSocket】WebSocket事件

《HTML5 WebSocket权威指南》学习笔记&2上一篇:【Html5 WebSocket】WebSocket构造函数WebSocket事件WebSocket编程遵循异步编程模式,只要WebSocket的连接已打开,应用程序就开始监听事件。客户端不需要主动轮询服务器获取更多信息。下面是WebSocket对象的四个不同的监听器: open message error close 上述四个事件同所有

zgljl2012的专栏 3207

零基础实战:YOLO检测头自定义替换全攻略

本文为零基础用户提供了YOLO检测头自定义替换的完整实战指南。通过将检测头比作汽车的“方向盘”,文章详细阐述了为何需要替换、如何寻找和集成新检测头模块、修改配置文件以及最终训练验证的全过程,旨在帮助用户针对特定任务(如小目标检测)提升模型性能。

perl8的博客 255

vue中websocket的使用---详解

WebSockets 是一种先进的技术,它可以在用户的浏览器和服务器之间打开交互式通信会话。使用此 API,可以向服务器发送消息并接收事件驱动的响应,而无需通过轮询服务器的方式以获得响应。WebSockets 这种技术中有一个接口名为WebSocket,它是一个用于连接 WebSocket 服务器的主要接口,之后可以在这个连接上发送和接受数据。接下来的案例则是使用该接口创建一个WebSocket对象来进行管理。//定义。

lwc18639470699的博客 1万+

不用FFmpeg!纯前端实现RTSP直播的3种现代方案对比(WebSocket/Wasm/WebCodecs)

本文深入探讨了纯前端实现RTSP直播的三种现代技术方案:WebSocket代理转发、Wasm解码和WebCodecs硬解。通过详细的架构设计、性能实测数据和兼容性分析,帮助开发者在安防监控、智慧城市等场景中选择最佳方案,实现低延迟、高效率的视频流处理。

lilh34434的博客 231

CISP-PTE考前实操练习题

CISP-PTE考前学习练习题目,渗透测试功工程师

2301_79546223的博客 2002

【websocket】spring boot 集成 websocket 的四种方式

1. 原生注解 pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> WebSocketConfig /* * * * * blog.coder4j.cn * * Copyright (C) 2016-20

Wanankl的博客 3834

飞牛Docker部署Zerotier后无法访问?三步搞定SSH权限配置!

本文详细解析了在飞牛NAS上通过Docker部署Zerotier后无法访问的常见问题及解决方案。重点介绍了SSH权限配置、tun模块加载和Docker容器权限设置等关键步骤,帮助用户快速解决虚拟网络接口的权限问题,确保Zerotier节点间的正常通信。

qq_27496129的博客 798

一文搞懂四种 WebSocket 使用方式

在上家公司做IM消息系统的时候,一直是使用 WebSocket 作为收发消息的基础组件,今天就和大家聊聊在 Java 中,使用 WebSocket 所常见的四种姿势,如果大家以后或者现在碰到有要使用 WebSoocket 的情况可以做个参考。上面的思维导图已经给大家列出了三种使用 WebSocket 的方式,下文会对它们的特点进行一一解读,不同的方式具有不同的特点,我们先按下不表。在这里,我想让大家思考一下我在思维导图中列举的第四种做 WebScoket 支持的方案可能是什么?不知道大家能不能猜对,后文将会

m0_69860228的博客 8736

QCustomPlot实现毫秒级时间轴数据的动态可视化与实时刷新优化

本文深入探讨了如何利用QCustomPlot库高效实现毫秒级时间轴数据的动态可视化与实时刷新。文章从基础配置讲起,详细介绍了时间轴初始化、数据平滑滚动、增量更新与内存管理等核心优化技巧,并分享了应对高频数据的缓冲策略及OpenGL硬件加速等高级优化手段,旨在解决实时图表卡顿问题,提升流畅度。

weixin_29012677的博客 425

lammps 案例:高熵合金FeNiCrCoCu建模及拉伸模拟

高熵合金力学性能模拟包括拉伸、剪切、摩擦等模拟,本文以高熵合金FeNiCrCoCu为例介绍高熵合金建模及拉伸过程模拟。 FeNiCrCoCu原子数量比为1:1:1:1:1 高熵合金的拉伸与单晶拉伸过程类似,主要包含以下步骤: 1.建模 高熵合金含有原子类型较多,建模时,一般以含量最多的原子为主建立初始模型。 使用set type/ratio或者set type/fraction的方式按一定比例替换为其它合金原子。 如随机选择20%的原子1,将其转换为原子2: set type 1 type/ratio 2

lammps_jiayou的博客 6067

前端WebSocket的事件触发机制

WebSocket API是纯事件驱动的。应用程序代码监听WebSocket对象上的事件,以便处理输入数据和连接状态的改变。WebSocket协议也是事件驱动的。客户端应用程序不需要轮询服务器来得到更新的数据。消息和事件将在服务器发送它们的时候异步到达。 WebSocket编程遵循异步编程模式,也就是说,只要WebSocket连接打开,应用程序就简单地监听事件。客户端不需要主动轮询服务器得到更多...

細水、長流√的专栏 1985

Win11此电脑怎么放在桌面上?Win11此电脑如何显示在电脑桌面上?

相信用习惯Win10的用户都喜欢电脑电脑桌面上的此电脑,但是很多用户更新完Win11正式版以后,发现桌面只有回收站和Microsoft Edge图标,没有此电脑。那么,Win11此电脑要如何显示呢?下面小编为大家简单介绍一下操作方法!5、桌面图标设置窗口,点击勾选计算机,然后点击确定,桌面就可以显示此电脑图标了。1、桌面空白处,点击右键,在打开的菜单项中,选择显示设置;4、个性化 - 主题,相关设置下,点击桌面图标设置;3、右侧点击主题(安装、创建、管理);2、设置窗口,左侧点击个性化;

shengyin714959的博客 13万+

WebSocket(WS)协议系列(二)事件机制

事件机制是一种编程模型,通常用于处理异步事件。在这种模型中,程序不会一直轮询或等待某个任务的完成,而是当某个事件发生时,系统会自动触发特定的处理函数(通常被称为回调函数)来响应这个事件。

是纯一的博客 1474

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Websocket-Rails事件路由详解:从JavaScript事件到Rails控制器动作的完整流程

想要构建实时Web应用却对事件路由感到困惑?Websocket-Rails为Rails开发者提供了完美的解决方案!🎯 这个强大的插件让JavaScript事件与Rails控制器动作之间的通信变得简单直观,实现真正的双向实时通信。本文将深入解析Websocket-Rails的事件路由机制,带你从客户端到服务器端,完整理解事件的生命周期。 ## 为什么选择Websocket-Rails事件路由?

gitblog_00420的博客 400

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

WebSocket 入门篇(一)

WebSocket 基础知识 1、WebSocket 实例化       JavaScript中实例化            var ws = new WebSocket(url,protocols);       实例化时可以接受两个变量       1)、指向目标url,也就是服务请求的地址,url有两种方案:                一、WS   非加密流量  例如:"ws:...

Mark_Xu的博客 2284

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

springboot集成websocket的四种方式小结

本文主要介绍了springboot集成websocket的四种方式小结,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

spring cloud 微服务技术开发 4988

websocket java详解_WebSocket详解

WebSocket详解WebSocket可提供一个在单一TCP连接全双工双向通信协议。全双工意味着客户端和服务器可以独立发送信息给对方。双向意味着客户端可以向服务器发送信息,反之亦然。WebSocket是定义于IETF RFC 6455协议和W3CJavaScript API。该协议在TCP层定义了一个开放的握手和基本的讯息。该API允许Web页使用WebSocket协议双向与远程主机通信。不同于...

weixin_42410443的博客 8485
上一篇: WebSocket 入门篇(一)
下一篇: Mysql内置函数(字符串、日期、数子)
Mark_XC
博客等级 码龄9年 16粉丝 32原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值