Linux下 C/C++ NTP网络时间协议详解

news2024/9/29 17:58:02

NTP(Network Time Protocol,网络时间协议)是由RFC 1305定义的时间同步协议。它是通过网络在计算机系统之间进行时钟同步的网络协议。NTP 在公共互联网上通常能够保持时间延迟在几十毫秒以内的精度,并在理想条件下,它能在局域网下达到低于一毫秒的延迟精度。它使用用户数据报协议(UDP)在端口 123 上发送和接受时间戳。它是个 C/S 架构的应用程序。

NTP工作原理

NTP的基本工作原理如图所示。Device A和Device B通过网络相连,它们都有自己独立的系统时钟,需要通过NTP实现各自系统时钟的自动同步。为便于理解,作如下假设:
 ·在Device A和Device B的系统时钟同步之前,Device A的时钟设定为10:00:00am,Device B的时钟设定为11:00:00am。
 ·Device B作为NTP时间服务器,即Device A将使自己的时钟与Device B的时钟同步。
 ·NTP报文在Device A和Device B之间单向传输所需要的时间为1秒。

系统时钟同步的工作过程如下:
 ·Device A发送一个NTP报文给Device B,该报文带有它离开Device A时的时间戳,该时间戳为10:00:00am(T1)。
 ·当此NTP报文到达Device B时,Device B加上自己的时间戳,该时间戳为11:00:01am(T2)。
 ·当此NTP报文离开Device B时,Device B再加上自己的时间戳,该时间戳为11:00:02am(T3)。
 ·当Device A接收到该响应报文时,Device A的本地时间为10:00:03am(T4)。
至此,Device A已经拥有足够的信息来计算两个重要的参数:
 ·NTP报文的往返时延Delay=(T4-T1)-(T3-T2)=2秒。
 ·Device A相对Device B的时间差offset=((T2-T1)+(T3-T4))/2=1小时。
这样,Device A就能够根据这些信息来设定自己的时钟,使之与Device B的时钟同步。
以上内容只是对NTP工作原理的一个粗略描述.

/**
  * T1,客户端发送请求时的 本地系统时间戳;
  * T2,服务端接收到客户端请求时的 本地系统时间戳;
  * T3,服务端发送应答数据包时的 本地系统时间戳;
  * T4,客户端接收到服务端应答数据包时的 本地系统时间戳。
  */

NTP 报文格式

/*
 * Dissecting NTP packets version 3 and 4 (RFC5905, RFC2030, RFC1769, RFC1361,
 * RFC1305).
 *
 * Those packets have simple structure:
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |LI | VN  |Mode |    Stratum    |     Poll      |   Precision   |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                          Root Delay                           |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                       Root Dispersion                         |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                    Reference Identifier                       |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                   Reference Timestamp (64)                    |
 * |                                                               |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                   Originate Timestamp (64)                    |
 * |                                                               |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                    Receive Timestamp (64)                     |
 * |                                                               |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                    Transmit Timestamp (64)                    |
 * |                                                               |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                 Key Identifier (optional) (32)                |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                 Message Digest (optional) (128/160)           |
 * |                                                               |
 * |                                                               |
 * |                                                               |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * NTP timestamps are represented as a 64-bit unsigned fixed-point number,
 * in seconds relative to 0h on 1 January 1900. The integer part is in the
 * first 32 bits and the fraction part in the last 32 bits.
 *
 *
 * NTP Control messages as defined in version 2, 3 and 4 (RFC1119, RFC1305) use
 * the following structure:
 *                      1                   2                   3
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |00 | VN  | 110 |R E M| OpCode  |           Sequence            |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |            Status             |        Association ID         |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |            Offset             |             Count             |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                                                               |
 * |                     Data (468 octets max)                     |
 * |                                                               |
 * |                               |        Padding (zeros)        |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                 Authenticator (optional) (96)                 |
 * |                                                               |
 * |                                                               |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 *
 * Not yet implemented: complete dissection of TPCTRL_OP_SETTRAP,
 * NTPCTRL_OP_ASYNCMSG, NTPCTRL_OP_UNSETTRAPSETTRAP Control-Messages
 *
 */

NTP Version 3
NTP Version 4

它的字段含义参考如下:

LI 闰秒标识器,占用2个bit
VN 版本号,占用3个bits,表示NTP的版本号,现在为3
Mode 模式,占用3个bits,表示模式
stratum(层),占用8个bits
Poll 测试间隔,占用8个bits,表示连续信息之间的最大间隔
Precision 精度,占用8个bits,,表示本地时钟精度
Root Delay根时延,占用8个bits,表示在主参考源之间往返的总共时延
Root Dispersion根离散,占用8个bits,表示在主参考源有关的名义错误
Reference Identifier参考时钟标识符,占用8个bits,用来标识特殊的参考源
参考时间戳,64bits时间戳,本地时钟被修改的最新时间。
原始时间戳,客户端发送的时间,64bits。
接受时间戳,服务端接受到的时间,64bits。
传送时间戳,服务端送出应答的时间,64bits。
认证符(可选项)

NTP的工作模式

NTP支持以下几种工作模式:

 1.客户端/服务器模式
 2.对等体模式
 3.广播模式
 4.组播模式

用户可以根据需要选择一种或几种工作模式进行时间同步,我们主要讲解客户端/服务器模式。

工作过程:

客户端上需要手工指定NTP服务器的地址。客户端向NTP服务器发送NTP时间同步报文。NTP服务器收到报文后会自动工作在服务器模式,并回复应答报文如果客户端可以从多个时间服务器获取时间同步,则客户端收到应答报文后,进行时钟过滤和选择,并与优选的时钟进行时间同步

一些常用的 NTP 服务器地址:

ntp.tencent.com
ntp1.tencent.com
ntp2.tencent.com
ntp3.tencent.com
ntp4.tencent.com
ntp5.tencent.com
ntp.aliyun.com
ntp1.aliyun.com
ntp2.aliyun.com
ntp3.aliyun.com
ntp4.aliyun.com
ntp5.aliyun.com
ntp6.aliyun.com
ntp7.aliyun.com
time.edu.cn
s2c.time.edu.cn
s2f.time.edu.cn

NTP客户端连接服务器获取网络时间戳

typedef enum xntp_mode_t
{
    ntp_mode_unknow     = 0,  ///< 未定义
    ntp_mode_initiative = 1,  ///< 主动对等体模式
    ntp_mode_passive    = 2,  ///< 被动对等体模式
    ntp_mode_client     = 3,  ///< 客户端模式
    ntp_mode_server     = 4,  ///< 服务器模式
    ntp_mode_broadcast  = 5,  ///< 广播模式或组播模式
    ntp_mode_control    = 6,  ///< 报文为 NTP 控制报文
    ntp_mode_reserved   = 7,  ///< 预留给内部使用
} xntp_mode_t;


static x_void_t output_tu(x_cstring_t xszt_info, xtime_vnsec_t xtm_vnsec)
{
    printf("%s : ", xszt_info);
    output_ns(xtm_vnsec);
    printf("\n");
}

/**********************************************************/
/**
 *打上信息标号,输出 NTP 时间戳 的 具体时间信息。
 */
static x_void_t output_tm(x_cstring_t xszt_info, xtime_stamp_t * xtm_stamp)
{
    printf("%s : ", xszt_info);
    output_ts(xtm_stamp);
    printf("\n");
}

...
xtime_vnsec_t ntpcli_get_time(
                    x_cstring_t xszt_host,
                    x_uint16_t xut_port,
                    x_uint32_t xut_tmout)
{
    x_int32_t     xit_errno = EPERM;
    xtime_vnsec_t xtm_vnsec = XTIME_INVALID_VNSEC;
    xntp_cliptr_t xntp_this = X_NULL;


    do
    {
        xntp_this = ntpcli_open();
        if (X_NULL == xntp_this)
        {
            break;
        }

        xit_errno = ntpcli_config(xntp_this, xszt_host, xut_port);
        if (0 != xit_errno)
        {
            errno = xit_errno;
            break;
        }

        xtm_vnsec = ntpcli_req_time(xntp_this, xut_tmout);
    } while (0);

    if (X_NULL != xntp_this)
    {
        ntpcli_close(xntp_this);
        xntp_this = X_NULL;
    }

    return xtm_vnsec;
}
/**********************************************************/
/**
 *初始化 NTP 的请求数据包。
 */
static x_void_t ntp_init_req_packet(xntp_pack_t * xnpt_dptr)
{
...

    xnpt_dptr->xct_lvmflag   = NTP_LI_VN_MODE(0, 3, ntp_mode_client);
    xnpt_dptr->xct_stratum   = 0;
    xnpt_dptr->xct_ppoll     = 4;
    xnpt_dptr->xct_percision = (x_char_t)(-6);

    xnpt_dptr->xut_rootdelay = (1 << 16);
    xnpt_dptr->xut_rootdisp  = (1 << 16);
    xnpt_dptr->xut_refid     = 0;

    xnpt_dptr->xtms_reference.xut_seconds  = 0;
    xnpt_dptr->xtms_reference.xut_fraction = 0;
    xnpt_dptr->xtms_originate.xut_seconds  = 0;
    xnpt_dptr->xtms_originate.xut_fraction = 0;
    xnpt_dptr->xtms_receive  .xut_seconds  = 0;
    xnpt_dptr->xtms_receive  .xut_fraction = 0;
    xnpt_dptr->xtms_transmit .xut_seconds  = 0;
    xnpt_dptr->xtms_transmit .xut_fraction = 0;
}

/**********************************************************/
/**
 * 发送 NTP 请求,获取服务器时间戳。
 */
xtime_vnsec_t ntpcli_req_time(xntp_cliptr_t xntp_this, x_uint32_t xut_tmout)
{
    x_int32_t xit_errno = EPERM;

    // 参数验证

    if (X_NULL == xntp_this)
    {
        errno = EINVAL;
        return XTIME_INVALID_VNSEC;
    }

    if (name_is_ipv4(xntp_this->xszt_host, X_NULL))
        xit_errno = ntpcli_get_4T(xntp_this, xntp_this->xszt_host, xut_tmout);
    else
        xit_errno = ntpcli_get_4T_by_name(xntp_this, xut_tmout);

    if (0 != xit_errno)
    {
        errno = xit_errno;
        return XTIME_INVALID_VNSEC;
    }
    return ntp_calc_4T(xntp_this->xtm_4time);
}

/** 常用的 NTP 服务器地址列表 */
x_cstring_t xszt_host[] =
{
    "ntp.tencent.com" ,
    "ntp1.tencent.com",
    "ntp2.tencent.com",
    "ntp3.tencent.com",
    "ntp4.tencent.com",
    "ntp5.tencent.com",
    "ntp1.aliyun.com" ,
    "ntp2.aliyun.com" ,
    "ntp3.aliyun.com" ,
    "ntp4.aliyun.com" ,
    "ntp5.aliyun.com" ,
    "ntp6.aliyun.com" ,
    "ntp7.aliyun.com" ,
    "time.edu.cn"     ,
    "s2c.time.edu.cn" ,
    "s2f.time.edu.cn" ,
    "s2k.time.edu.cn" ,
    X_NULL
};

/** 网络地址(IP 或 域名)类型 */
typedef x_char_t x_host_t[TEXT_LEN_256];

/**
 * 命令选项的各个参数。
 */
typedef struct xopt_args_t
{
    x_bool_t   xbt_usage; ///< 是否显示帮助信息
    x_uint16_t xut_port;  ///< NTP 服务器端口号(默认值为 123)
    x_host_t   xntp_host; ///< NTP 服务器地址
    x_int32_t  xit_rept;  ///< 请求重复次数(默认值为 1)
    x_uint32_t xut_tmout; ///< 网络请求的超时时间(单位为 毫秒,默认值取 3000)
} xopt_args_t;

/** 简单的判断 xopt_args_t 的有效性 */
#define XOPT_VALID(xopt) (('\0' != (xopt).xntp_host[0]) && ((xopt).xit_rept > 0))



/**********************************************************/
/**
 * 字符串忽略大小写的比对操作。
 */
static x_int32_t xstr_icmp(x_cstring_t xszt_lcmp, x_cstring_t xszt_rcmp)
{
    x_int32_t xit_lvalue = 0;
    x_int32_t xit_rvalue = 0;

    if (xszt_lcmp == xszt_rcmp)
        return 0;
    if (X_NULL == xszt_lcmp)
        return -1;
    if (X_NULL == xszt_rcmp)
        return 1;

    do
    {
        if (((xit_lvalue = (*(xszt_lcmp++))) >= 'A') && (xit_lvalue <= 'Z'))
            xit_lvalue -= ('A' - 'a');

        if (((xit_rvalue = (*(xszt_rcmp++))) >= 'A') && (xit_rvalue <= 'Z'))
            xit_rvalue -= ('A' - 'a');

    } while (xit_lvalue && (xit_lvalue == xit_rvalue));

    return (xit_lvalue - xit_rvalue);
}

/**********************************************************/
/**
 *显示应用程序的 命令行格式。
 */
x_void_t usage(x_cstring_t xszt_app)
{
    x_int32_t xit_iter = 1;

    printf("Usage:\n %s [-h] [-n <number>] -s <host> [-p <port>] [-t <msec>]\n", xszt_app);
    printf("\t-h          Output usage.\n");
    printf("\t-n <number> The times of repetition.\n");
    printf("\t-s <host>   The host of NTP server, IP or domain.\n");
    printf("\t-p <port>   The port of NTP server, default 123.\n");
    printf("\t-t <msec>   Network request timeout in milliseconds, default 3000.\n");

    printf("\nCommon NTP server:\n");

    for (xit_iter = 0; X_NULL != xszt_host[xit_iter]; ++xit_iter)
    {
        printf("\t%s\n", xszt_host[xit_iter]);
    }

    printf("\n");
}

/**********************************************************/
/**
 * 从命令行中,提取工作的选项参数信息。
 */
x_void_t get_opt(x_int32_t xit_argc, x_cstring_t xszt_argv[], xopt_args_t * xopt_args)
{
...

    for (; xit_iter < xit_argc; ++xit_iter)
    {
        if (!xopt_args->xbt_usage && (0 == xstr_icmp("-h", xszt_argv[xit_iter])))
        {
            xopt_args->xbt_usage = X_TRUE;
        }
        else if (0 == xstr_icmp("-s", xszt_argv[xit_iter]))
        {
            if ((xit_iter + 1) < xit_argc)
            {
...
            }
        }
        else if (0 == xstr_icmp("-n", xszt_argv[xit_iter]))
        {
            if ((xit_iter + 1) < xit_argc)
                xopt_args->xit_rept = atoi(xszt_argv[++xit_iter]);
        }
        else if (0 == xstr_icmp("-p", xszt_argv[xit_iter]))
        {
            if ((xit_iter + 1) < xit_argc)
                xopt_args->xut_port = (x_uint16_t)atoi(xszt_argv[++xit_iter]);
        }
        else if (0 == xstr_icmp("-t", xszt_argv[xit_iter]))
        {
            if ((xit_iter + 1) < xit_argc)
                xopt_args->xut_tmout = (x_uint32_t)atoi(xszt_argv[++xit_iter]);
        }
    }
}

int main(int argc, char * argv[])
{
...
    do
    {

        get_opt(argc, argv, &xopt_args);
        if (!XOPT_VALID(xopt_args))
        {
            usage(argv[0]);
            break;
        }

        if (xopt_args.xbt_usage)
        {
            usage(argv[0]);
        }

        xntp_this = ntpcli_open();
        if (X_NULL == xntp_this)
        {
            printf("ntpcli_open() return X_NULL, errno : %d\n", errno);
            break;
        }

        ntpcli_config(xntp_this, xopt_args.xntp_host, xopt_args.xut_port);

        for (xit_iter = 0; xit_iter < xopt_args.xit_rept; ++xit_iter)
        {
            xtm_vnsec = ntpcli_req_time(xntp_this, xopt_args.xut_tmout);
            if (XTMVNSEC_IS_VALID(xtm_vnsec))
            {
                xtm_ltime = time_vnsec();
                xtm_descr = time_vtod(xtm_vnsec);
                xtm_local = time_vtod(xtm_ltime);

                printf("\n[%d] %s:%d : \n",
                       xit_iter + 1,
                       xopt_args.xntp_host,
                       xopt_args.xut_port);
                printf("\tNTP response : [ %04d-%02d-%02d %d %02d:%02d:%02d.%03d ]\n",
                       xtm_descr.ctx_year  ,
                       xtm_descr.ctx_month ,
                       xtm_descr.ctx_day   ,
                       xtm_descr.ctx_week  ,
                       xtm_descr.ctx_hour  ,
                       xtm_descr.ctx_minute,
                       xtm_descr.ctx_second,
                       xtm_descr.ctx_msec  );

                printf("\tLocal time   : [ %04d-%02d-%02d %d %02d:%02d:%02d.%03d ]\n",
                       xtm_local.ctx_year  ,
                       xtm_local.ctx_month ,
                       xtm_local.ctx_day   ,
                       xtm_local.ctx_week  ,
                       xtm_local.ctx_hour  ,
                       xtm_local.ctx_minute,
                       xtm_local.ctx_second,
                       xtm_local.ctx_msec  );

                printf("\tDeviation    : %lld us\n",
                       ((x_int64_t)(xtm_ltime - xtm_vnsec)) / 10LL);
            }
            else
            {
                printf("\n[%d] %s:%d : errno = %d\n",
                       xit_iter + 1,
                       xopt_args.xntp_host,
                       xopt_args.xut_port,
                       errno);
            }
        }

        
    } while (0);

    if (X_NULL != xntp_this)
    {
        ntpcli_close(xntp_this);
        xntp_this = X_NULL;
    }
...
    return 0;
}
...

运行结果:

If you need the complete source code, please add the WeChat number (c17865354792)

在客户端/服务器模式中,客户端向服务器发送时钟同步报文,报文中的Mode字段设置为3(客户模式)。服务器端收到报文后会自动工作在服务器模式,并发送应答报文,报文中的Mode字段设置为4(服务器模式)。客户端收到应答报文后,进行时钟过滤和选择,并同步到优选的服务器。

在该模式下,客户端能同步到服务器,而服务器无法同步到客户端。

  • tcpdump抓包分析

NTP协议应用于分布式时间服务器和客户端之间,实现客户端和服务器的时间同步,从而使网络内所有设备的时钟基本保持一致。下面的报文是客户端连接服务器时产生的交互过程。

  • NTP协议解析

If you need the complete source code, please add the WeChat number (c17865354792)

总结

NTP客户端启动与NTP服务器的时间请求交换。交换的结果是,客户端能够计算链路延迟及其本地偏移,并调整其本地时钟以匹配服务器计算机上的时钟。

Welcome to follow WeChat official account【程序猿编码

参考:1.RFC 1305
2.http://ntp.neu.edu.cn/archives/92/
3.http://ntp.neu.edu.cn/archives/95

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

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

相关文章

Molecule:使用Jetpack Compose构建StateFlow流

Molecule:使用Jetpack Compose构建StateFlow流 看下面的jetpack compose片段&#xff1a; Composable fun MessageCard(message: Message) {Column {Text(text message.author)Text(text message.body)} }这段代码最有趣的部分是它实际上是reactive。其反应性为 通过Composa…

树链剖分(维护树上信息)

学习前请先掌握线段树&#xff1a;线段树&#xff08;维护区间信息&#xff09; 一&#xff0c;思想&#xff1a; 将一颗树拆成多条线性链以方便维护&#xff08;如线段树&#xff09;。 先给出以下定义&#xff08;通过这些定义我们就可以组成链&#xff09;&#xff1a; …

Docker概念介绍

目录 1、传统方式、虚拟化、容器部署方式的区别 2、为什么会有docker 3、什么是docker 4、docker的优势 5、Docker组成部分 6、docker镜像的原理介绍 7、 容器应用场景 8、Docker资源汇总 了解docker之前&#xff0c;我们要先了解部署方式有哪些&#xff0c;各有什么优缺点…

Windows 右键菜单扩展容器 [开源]

今天给大家分享一个我做的小工具&#xff0c;可以自定义扩展右键菜单的功能来提高工作效率&#xff0c;效果图如下&#xff1a; 如上图&#xff0c;右键菜单多了几个我自定义的菜单&#xff1a; 复制文件路径 复制文件夹路径 我的工具箱 <走配置文件动态创建子菜单&#x…

cesium封装实现配置格网及插值高程面实现

一、数据结构建模二、插值算法得到的插值结果三、图层配置primitiveGrid:{isRLayerPanel: true,primitives:[],url: /static/data/Grid.json,dataPath: ,dataIdField: code,options:{id:primitiveGrid,name:格网,type:grid,isShow: false},location: {"destination":…

Hive中的基础函数(一)

一、hive中的内置函数根据应用归类整体可以分为8大种类型。 1、 String Functions 字符串函数 主要针对字符串数据类型进行操作&#xff0c;比如下面这些&#xff1a; 字符串长度函数&#xff1a;length •字符串反转函数&#xff1a;reverse •字符串连接函数&#xff1a;…

Word处理控件Aspose.Words功能演示:使用 C++ 在 Word 文档 (DOC/DOCX) 中插入表格

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

基于python的多线程数据库数据录入

说明&#xff1a; 使用python编程结合多线程技术&#xff0c;将已经在python文件中的数据批量写入到数据库&#xff0c;便于数据关系结构化管理。 环境配置&#xff1a; certifi2019.6.16 chardet3.0.4 idna2.8 PyMySQL0.9.3 requests2.22.0 urllib31.25.3 将所需要的环境保…

vue模板语法和数据绑定和el、data的两种

vue模板语法有两大类&#xff1a; 1.插值语法&#xff1a; 功能&#xff1a;用于解拆标签体内容 写法&#xff1a;{{xxx}}&#xff0c;xxx是js表达式&#xff0c;且可以直接读取到data中的所有属性 2.指令语法&#xff1a; 功能&#xff1a;用于解拆标签&#xff08;包括&…

《商用密码应用与安全性评估》第一章密码基础知识1.1应用概念

密码的概念与作用 概念 密码&#xff1a;采用特定变换的方法对信息进行加密保护、安全认证的技术、产品和服务。 密码技术&#xff1a;密码编码、实现、协议、安全防护、分析破译、以及密钥产生、分发、传递、使 用、销毁等技术。 密码技术核心&#xff1a;密码算法…

家用洗地机什么品牌质量好耐用?最适合家用的洗地机

近些年&#xff0c;随着消费水平的不断升级&#xff0c;我们对家电产品的要求也在逐步提高&#xff0c;就以这几年非常流行的洗地机为例&#xff0c;如今的人们在选洗地机时&#xff0c;会综合考虑价位、技术、配置、颜值、功能等多个方面&#xff0c;那么市场上家用洗地机什么…

JAVACC

JavaCC全称为Java Compiler Compiler&#xff0c;它是一个生成器&#xff0c;用于生成词法分析器&#xff08;lexical analysers&#xff09;和语法分析器&#xff08;parsers&#xff09;&#xff1b;JavaCC本身并不是词法分析器和语法分析器&#xff0c;它是一个生成器&#…

mysql数据库之索引结构

MySQL的索引是在存储引擎层实现的&#xff0c;不同的存储引擎有不同的结构。 一、常见索引。 索引结构描述BTree索引最常见的索引类型&#xff0c;大部分引擎都支持B树索引Hash索引底层数据结构是用哈希表实现的&#xff0c;只有精确匹配索引列的查询才有效&#xff0c;不支持…

Web版和客户端哪种SQL工具更好?ChatGPT有话要说

2023年年初公司发布了一款Web版SQL工具&#xff0c;短期内就赢得了众多用户的喜爱和下载。不过&#xff0c;也有SQL用户在评论区中提出自己的观点&#xff0c;认为Web版工具都不可靠&#xff0c;甚至看见Web版工具就劝返… … 工具Web化逐渐成为一种趋势&#xff0c;比如&…

如何使用Bypass-Url-Parser实现URL绕过并访问40X受保护页面

关于Bypass-Url-Parser Bypass-Url-Parser是一款功能强大的URL绕过工具&#xff0c;该工具可以使用多种方法实现URL绕过并访问目标站点的40X受保护页面。 工具下载 由于该工具基于Python 3 开发&#xff0c;因此我们首先需要在本地设备上安装并配置好Python 3环境。接下来&a…

PLC实验—西门子S7 1200读取超声波传感器数据

PLC实验—西门子S7 1200读取超声波传感器数据 US-016超声波测距模块 实验箱上是US-016超声波测距模块&#xff0c;其有关信息可以看实验室的博客 US-016超声波测距模块 1号Pin&#xff1a;接VCC电源&#xff08;直流5V&#xff09; 2号Pin&#xff1a;量程设置引脚&#xff…

GWT安装过程

1:安装前准备 &#xff08;可以问我要&#xff09; appengine-java-sdk-1.9.8 com.google.gdt.eclipse.suite.4.3.update.site_3.8.0 gwt-2.5.1 eclipse-jee-kepler-SR2-win32-x86_64.zip 2&#xff1a;安装环境上 打开eclipse Help –Install New Software… 选择Add –…

如何使用工时表管理项目和非项目的资源?

对新机会做出反应的能力是企业竞争优势的关键。项目不断涌现&#xff0c;企业需要了解具体的可用性以及是否有资源来接受新事物。更进一步来说&#xff0c;企业需要知道员工将时间花在哪里。 使用 8Manage工时表解决方案&#xff0c;你将始终拥有做出正确业务决策所需的全面知…

vue源码分析-响应式系统工作原理

上一章&#xff0c;我们讲到了Vue初始化做的一些操作&#xff0c;那么我们这一章来讲一个Vue核心概念响应式系统。 我们先来看一下官方对深入响应式系统的解释: 当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项&#xff0c;Vue 将遍历此对象所有的属性。 并使用 O…

LeetCode:构造最大二叉树;使用中序和后序数组构造二叉树;使用前序和中序数组遍历二叉树。

构造二叉树最好都是使用前序遍历&#xff1b;中左右的顺序。 654. 最大二叉树 中等 636 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建…