SRv6项目实践(二):基本的P4框架

news2024/9/20 15:00:28

1.数据包头的定义

在实现SRv6之前,有很多的工作需要做,首先先阅读一下p4的代码总体框架,数据包的包头格式一共有如下这些,我们需要把他们的协议逐一完善

struct parsed_headers_t {
    cpu_out_header_t cpu_out;
    cpu_in_header_t cpu_in;
    ethernet_t ethernet;
    ipv4_t ipv4;
    ipv6_t ipv6;
    srv6h_t srv6h;
    srv6_list_t[SRV6_MAX_HOPS] srv6_list;
    tcp_t tcp;
    udp_t udp;
    icmp_t icmp;
    icmpv6_t icmpv6;
    ndp_t ndp;
}

展开来说,这些协议的包头格式如下所示,其中一个很重要的数据包头是packet_in和out

header ethernet_t {
    mac_addr_t  dst_addr;
    mac_addr_t  src_addr;
    bit<16>     ether_type;
}

header ipv4_t {
    bit<4>   version;
    bit<4>   ihl;
    bit<6>   dscp;
    bit<2>   ecn;
    bit<16>  total_len;
    bit<16>  identification;
    bit<3>   flags;
    bit<13>  frag_offset;
    bit<8>   ttl;
    bit<8>   protocol;
    bit<16>  hdr_checksum;
    bit<32>  src_addr;
    bit<32>  dst_addr;
}

header ipv6_t {
    bit<4>    version;
    bit<8>    traffic_class;
    bit<20>   flow_label;
    bit<16>   payload_len;
    bit<8>    next_hdr;
    bit<8>    hop_limit;
    bit<128>  src_addr;
    bit<128>  dst_addr;
}

header srv6h_t {
    bit<8>   next_hdr;
    bit<8>   hdr_ext_len;
    bit<8>   routing_type;
    bit<8>   segment_left;
    bit<8>   last_entry;
    bit<8>   flags;
    bit<16>  tag;
}

header srv6_list_t {
    bit<128>  segment_id;
}

header tcp_t {
    bit<16>  src_port;
    bit<16>  dst_port;
    bit<32>  seq_no;
    bit<32>  ack_no;
    bit<4>   data_offset;
    bit<3>   res;
    bit<3>   ecn;
    bit<6>   ctrl;
    bit<16>  window;
    bit<16>  checksum;
    bit<16>  urgent_ptr;
}

header udp_t {
    bit<16> src_port;
    bit<16> dst_port;
    bit<16> len;
    bit<16> checksum;
}

header icmp_t {
    bit<8>   type;
    bit<8>   icmp_code;
    bit<16>  checksum;
    bit<16>  identifier;
    bit<16>  sequence_number;
    bit<64>  timestamp;
}

header icmpv6_t {
    bit<8>   type;
    bit<8>   code;
    bit<16>  checksum;
}

header ndp_t {
    bit<32>      flags;
    ipv6_addr_t  target_ipv6_addr;
    // NDP option.
    bit<8>       type;
    bit<8>       length;
    bit<48>      target_mac_addr;
}

// Packet-in header. Prepended to packets sent to the CPU_PORT and used by the
// P4Runtime server (Stratum) to populate the PacketIn message metadata fields.
// Here we use it to carry the original ingress port where the packet was
// received.
@controller_header("packet_in")
header cpu_in_header_t {
    port_num_t  ingress_port;
    bit<7>      _pad;
}

// Packet-out header. Prepended to packets received from the CPU_PORT. Fields of
// this header are populated by the P4Runtime server based on the P4Runtime
// PacketOut metadata fields. Here we use it to inform the P4 pipeline on which
// port this packet-out should be transmitted.
@controller_header("packet_out")
header cpu_out_header_t {
    port_num_t  egress_port;
    bit<7>      _pad;
}

2.包的解析

对数据包的解析工作主要是:

  1. 端口检查:判定数据包的进端口是否是CPU_PORT,这里CPU_PORT=255,如果数据包的进端口是从这里进来的,那么它是一个从数据平面下达的packet_out类型的数据包(跳转到2),如果是普通数据包,直接解析以太网地址即可(跳转到3)
  2. packet_out:解析packet_out数据包,观察这个数据包的cpu_out的字段,发现它的字段中包括了一个egress_port,这里使用它来定义接下去packet_out的包要如何转发(跳转到3)
  3. ethernet:正常的以太网地址解析,根据其以太网地址解析IPv4(跳转到4)或者IPv6(跳转到5)的数据包
  4. ipv4:解析ipv4数据包头,并用ip_proto来标记数据包的ip协议的上层协议类型,区分它是TCP(跳转到8)还是UDP(跳转到9)还是ICMP(跳转到10)
  5. ipv6:解析ipv6数据包头,并用ip_proto来标记数据包的ipv6协议的下一个协议类型,区分它是TCP(跳转到8)还是UDP(跳转到9)还是ICMPv6(跳转到11),当然,可能上层协议是自己的拓展头(跳转到6),在这里这个拓展头是SRH,协议号是43
  6. srv6:解析出来它的srv6的拓展头,紧接着去解析它的SID序列(跳转到7)

  7. srv6_list:核心思想是,根据自己的SL一步一步拆开,然后解析,直到最后一个为之,srv6的解析才算结束。

    • bool next_segment = 
      (bit<32>)hdr.srv6h.segment_left - 1 == 
      (bit<32>)hdr.srv6_list.lastIndex;
      首先判断SL的值是否是SID序列中当前对应的索引,如果是了话,说明这个SID是接下来我要去的地方,就把它替换到自己的包头的IPv6目的地址上mark_current_srv6
    • 如果上述判断没有成功,就继续逐个检查下去:check_last_srv6。检查的过程中,如果发现这是最后一个了,那就去解析srv6以外的东西了,也就是上层协议

      bool last_segment = 
      (bit<32>)hdr.srv6h.last_entry == 
      (bit<32>)hdr.srv6_list.lastIndex;
    • 解析srv6以外的东西:根据hdr.srv6h.next_hdr,判断下一个协议是TCP(跳转到8)还是UDP(跳转到9)还是ICMP(跳转到10)。小注释:hdr.srv6_list.next用完以后,srv6_list和lastIndex都会自动加一
  8. TCP:解析tcp协议,把交换机内的local_metadata的传输层源和目的端口都赋值

  9. UDP:解析udp协议,把交换机内的local_metadata的传输层源和目的端口都赋值

  10. ICMP:解析icmp协议,把交换机内local_metadata的icmp类型赋值

  11. ICMPv6:解析icmpv6协议,把交换机内local_metadata的icmp类型赋值,这里icmpv6有3种类型,分别是ICMP6_TYPE_NS和ICMP6_TYPE_NA和其他,他们都被视为NDP解析(跳转到12),无视其他类型

  12. NDP:解析NDP的首部,解析结束

parser ParserImpl (packet_in packet,
                   out parsed_headers_t hdr,
                   inout local_metadata_t local_metadata,
                   inout standard_metadata_t standard_metadata)
{
    state start {
        transition select(standard_metadata.ingress_port) {
            CPU_PORT: parse_packet_out;
            default: parse_ethernet;
        }
    }

    state parse_packet_out {
        packet.extract(hdr.cpu_out);
        transition parse_ethernet;
    }

    state parse_ethernet {
        packet.extract(hdr.ethernet);
        transition select(hdr.ethernet.ether_type){
            ETHERTYPE_IPV4: parse_ipv4;
            ETHERTYPE_IPV6: parse_ipv6;
            default: accept;
        }
    }

    state parse_ipv4 {
        packet.extract(hdr.ipv4);
        local_metadata.ip_proto = hdr.ipv4.protocol;
        transition select(hdr.ipv4.protocol) {
            IP_PROTO_TCP: parse_tcp;
            IP_PROTO_UDP: parse_udp;
            IP_PROTO_ICMP: parse_icmp;
            default: accept;
        }
    }

    state parse_ipv6 {
        packet.extract(hdr.ipv6);
        local_metadata.ip_proto = hdr.ipv6.next_hdr;
        transition select(hdr.ipv6.next_hdr) {
            IP_PROTO_TCP: parse_tcp;
            IP_PROTO_UDP: parse_udp;
            IP_PROTO_ICMPV6: parse_icmpv6;
            IP_PROTO_SRV6: parse_srv6;
            default: accept;
        }
    }

    state parse_tcp {
        packet.extract(hdr.tcp);
        local_metadata.l4_src_port = hdr.tcp.src_port;
        local_metadata.l4_dst_port = hdr.tcp.dst_port;
        transition accept;
    }

    state parse_udp {
        packet.extract(hdr.udp);
        local_metadata.l4_src_port = hdr.udp.src_port;
        local_metadata.l4_dst_port = hdr.udp.dst_port;
        transition accept;
    }

    state parse_icmp {
        packet.extract(hdr.icmp);
        local_metadata.icmp_type = hdr.icmp.type;
        transition accept;
    }

    state parse_icmpv6 {
        packet.extract(hdr.icmpv6);
        local_metadata.icmp_type = hdr.icmpv6.type;
        transition select(hdr.icmpv6.type) {
            ICMP6_TYPE_NS: parse_ndp;
            ICMP6_TYPE_NA: parse_ndp;
            default: accept;
        }
    }

    state parse_ndp {
        packet.extract(hdr.ndp);
        transition accept;
    }

    state parse_srv6 {
        packet.extract(hdr.srv6h);
        transition parse_srv6_list;
    }

    state parse_srv6_list {
        packet.extract(hdr.srv6_list.next);
        bool next_segment = (bit<32>)hdr.srv6h.segment_left - 1 == (bit<32>)hdr.srv6_list.lastIndex;
        transition select(next_segment) {
            true: mark_current_srv6;
            default: check_last_srv6;
        }
    }

    state mark_current_srv6 {
        local_metadata.next_srv6_sid = hdr.srv6_list.last.segment_id;
        transition check_last_srv6;
    }

    state check_last_srv6 {
        // working with bit<8> and int<32> which cannot be cast directly; using
        // bit<32> as common intermediate type for comparision
        bool last_segment = (bit<32>)hdr.srv6h.last_entry == (bit<32>)hdr.srv6_list.lastIndex;
        transition select(last_segment) {
           true: parse_srv6_next_hdr;
           false: parse_srv6_list;
        }
    }

    state parse_srv6_next_hdr {
        transition select(hdr.srv6h.next_hdr) {
            IP_PROTO_TCP: parse_tcp;
            IP_PROTO_UDP: parse_udp;
            IP_PROTO_ICMPV6: parse_icmpv6;
            default: accept;
        }
    }
}

小插曲:在继续讲之前,可能有很多人对这些协议都有个大概的了解,但是对ICMPv6可能比较陌生,所以,如果知道ICMPv6协议的朋友,请直接看后面。

以下是IPV6深入-NDP邻居发现协议 - 知乎 (zhihu.com)的一些截取

在IPv4中,当主机需要和目标主机通信时,必须先通过ARP协议获得目的主机的链路层地址。在IPv6中,同样需要从IP地址解析到链路层地址的功能。邻居发现协议实现了这个功能。

与IPv4的ARP相比,IPv6地址解析技术工作在OSI参考模型的网络层,与链路层协议无关。这一特点的益处如下:

(1)在第三层实现地址解析可以利用三层标准的安全认证机制来防止ARP攻击和ARP欺骗。

(2)IPv6的地址解析利用三层组播寻址限制了报文的传播范围,可节省网络带宽。

IPV6地址解析的具体过程如下:

(1)节点A向节点B发送NS报文,源地址为A的IPV6单播地址(可以是唯一本地地址也可以是链路本地地址),目的地址是B的被请求节点组播地址。源mac地址是节点A mac地址,目的mac地址是节点B的被请求节点组播mac地址。

(2)节点B收到节点A的NS报文后即知道节点A的IPV6单播地址、mac地址及被请求节点组播地址等,此时,节点B会回复NA报文,NA报文中源地址为节点B的IPV6单播地址、 MAC地址,目的地址为节点A的IPV6单播地址、mac地址

(3)节点A收到节点B的NA报文后,即知晓了节点B的IPV6单播地址、mac地址

NUD(Neighbor Unreachable Detection,邻居不可达检测)是节点确定邻居可达性的过程。邻居不可达检测机制通过邻居可达性状态机来描述邻居的可达性。

邻居可达性状态机保存在邻居缓存表中,共有如下6种状态:

(1)INCOMPLETE(未完成状态):表示正在解析地址,但邻居链路层地址尚未确定。

(2)REACHABLE(可达状态):表示地址解析成功,该邻居可达。

(3)STALE(失效状态):表示可达时间耗尽,未确定邻居是否可达。

(4)DELAY(延迟状态):表示未确定邻居是否可达。DELAY状态不是一个稳定的状态,而是一个延时等待状态。

(5)PROBE(探测状态):节点会向处于PROBE状态的邻居持续发送NS报文。

(6)EMPTY(空闲状态):表示节点上没有相关邻接点的邻居缓存表项。

之后的东西大家可以上对应的网站查询,本项目只关注NA和NS

3.进端口控制流

首先,在进端口控制流中,先写几个基本的表(在SRv6项目实践系列中还会不断添加),这里的注解用于在控制面能够得到匹配到该表的计数器,这个是一个L2的根据具体以太网目的地址转发的表,很简单吧,它是一个用户单播的表。

action set_egress_port(port_num_t port_num) {
        standard_metadata.egress_spec = port_num;
    }

    table l2_exact_table {
        key = {
            hdr.ethernet.dst_addr: exact;
        }
        actions = {
            set_egress_port;
            @defaultonly drop;
        }
        const default_action = drop;
        // The @name annotation is used here to provide a name to this table
        // counter, as it will be needed by the compiler to generate the
        // corresponding P4Info entity.
        @name("l2_exact_table_counter")
        counters = direct_counter(CounterType.packets_and_bytes);
    }

 这是一个用于组播的表,它被运用于组播中,比如NS的消息,在以太网中,NS组播的L2地址是以33:33为掩码的,在set_multicast_group中,设定一下它的组播的id就好啦,至于id对应的组播哪些地址,还不用说。

action set_multicast_group(mcast_group_id_t gid) {
        // gid will be used by the Packet Replication Engine (PRE) in the
        // Traffic Manager--located right after the ingress pipeline, to
        // replicate a packet to multiple egress ports, specified by the control
        // plane by means of P4Runtime MulticastGroupEntry messages.
        standard_metadata.mcast_grp = gid;
        local_metadata.is_multicast = true;
    }

    table l2_ternary_table {
        key = {
            hdr.ethernet.dst_addr: ternary;
        }
        actions = {
            set_multicast_group;
            @defaultonly drop;
        }
        const default_action = drop;
        @name("l2_ternary_table_counter")
        counters = direct_counter(CounterType.packets_and_bytes);
    }

这里,有一个及其重要的东西叫 ACLACL(访问控制列表)基础篇-超有趣学网络 - 知乎 (zhihu.com)

ACL,是Access Control List的简写,中文名称叫做“访问控制列表”。它是由一系列条件规则(即描述报文匹配条件的判断语句)组成, 这些条件规则可以是报文的源地址、目的地址、端口号等,是一种应用在网络设备各种软硬接口上的的指令列表。

访问控制列表的使用场景:

根据ACL中的匹配条件对进站和出站的报文进行过滤处理。打个比方,ACL其实是一种报文过滤器,ACL规则就是过滤器的滤芯。安装什么样的滤芯(即根据报文特征配置相应的ACL规则),ACL就能过滤出什么样的报文。

 ACL的主要功能:根据数据包的信息,决定把他们丢掉或者发送到cpu中,或者复制一份发到cpu,然后转发。在这里CPU就是p4runtime

action send_to_cpu() {
        standard_metadata.egress_spec = CPU_PORT;
    }

    action clone_to_cpu() {
        // Cloning is achieved by using a v1model-specific primitive. Here we
        // set the type of clone operation (ingress-to-egress pipeline), the
        // clone session ID (the CPU one), and the metadata fields we want to
        // preserve for the cloned packet replica.
        clone3(CloneType.I2E, CPU_CLONE_SESSION_ID, { standard_metadata.ingress_port });
    }

    table acl_table {
        key = {
            standard_metadata.ingress_port: ternary;
            hdr.ethernet.dst_addr:          ternary;
            hdr.ethernet.src_addr:          ternary;
            hdr.ethernet.ether_type:        ternary;
            local_metadata.ip_proto:        ternary;
            local_metadata.icmp_type:       ternary;
            local_metadata.l4_src_port:     ternary;
            local_metadata.l4_dst_port:     ternary;
        }
        actions = {
            send_to_cpu;
            clone_to_cpu;
            drop;
        }
        @name("acl_table_counter")
        counters = direct_counter(CounterType.packets_and_bytes);
    }

最后,apply这几个表,可以看到首先匹配单播,然后匹配多播,最后都要运用ACL实现访问控制

 apply {


        bool do_l3_l2 = true;

        if (do_l3_l2) {

            // L2 bridging logic. Apply the exact table first...
            if (!l2_exact_table.apply().hit) {
                // ...if an entry is NOT found, apply the ternary one in case
                // this is a multicast/broadcast NDP NS packet.
                l2_ternary_table.apply();
            }
        }
acl_table.apply();
}

4.出端口控制流

出端口的实现,相对简单,本地的组播不可能会组播回入端口。

control EgressPipeImpl (inout parsed_headers_t hdr,
                        inout local_metadata_t local_metadata,
                        inout standard_metadata_t standard_metadata) {
    apply {


        // If this is a multicast packet (flag set by l2_ternary_table), make
        // sure we are not replicating the packet on the same port where it was
        // received. This is useful to avoid broadcasting NDP requests on the
        // ingress port.
        if (local_metadata.is_multicast == true &&
              standard_metadata.ingress_port == standard_metadata.egress_port) {
            mark_to_drop(standard_metadata);
        }
    }
}

最后的校验和和封包就不用看了,从这个基本框架上来看,目前,还没能实现与控制面的交互,接下来的SRv6项目实践(三),我们将手动使用P4runtime控制数据平面。

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

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

相关文章

PostgreSQL环境搭建和主备构建

目录 1 Windows 上安装 PostgreSQL2 docker安装PostgreSQL2.1 检索当前镜像2.2. 拉取当前镜像2.3 创建挂载文件夹2.4 启动镜像2.5 查看日志2.7 查看进程2.8 使用连接 3 postgresql主从主备搭建3.1 安装好网络源&#xff08;主1.11、从1.12&#xff09;3.2 安装postgresql&#…

(数字图像处理MATLAB+Python)第五章图像增强-第二节:基于直方图修正的图像增强

文章目录 一&#xff1a;灰度直方图&#xff08;1&#xff09;定义&#xff08;2&#xff09;程序&#xff08;3&#xff09;性质 二&#xff1a;直方图修正法理论三&#xff1a;直方图均衡化&#xff08;1&#xff09;直方图均衡化变换函数T(r)的求解&#xff08;2&#xff09…

设计模式-创建型模式之简单工厂模式( Simple Factory Pattern )

1.创建型模式简介创建型模式(Creational Pattern)对类的实例化过程进行了抽象&#xff0c;能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰&#xff0c;外界对于这些对象只需要知道它们共同的接口&#xff0c;而不清楚其具体的实现细节&#xff0c;使整…

HCIP之MPLS中的LDP协议

LDP协议 LDP协议 --- 标签分发协议 MPLS控制层面需要完成的工作主要就是分配标签和传递标签。分配标签的前提是本地路由表中得先存在标签&#xff0c;传递标签的前提也是得先具备路由基础。所以&#xff0c;LDP想要正常工作&#xff0c;则需要IGP作为基础。 LDP协议主要需要完…

信号处理流程

1.降噪处理 我们在录制音频数据的同时&#xff0c;大量噪声都会掺杂进来&#xff0c;不同环境和情境下产生的噪声也不尽相同&#xff0c;噪声信号中的无规则波纹信息影响了声学信号所固有的声学特性&#xff0c;使得待分析的声音信号质量下降&#xff0c;并且噪声对声音识别系统…

02-数据库连接池+lombok工具

数据库连接池 概念&#xff1a; 数据库连接池是个容器&#xff0c;负责分配、管理数据库连接&#xff08;Connection&#xff09; 它允许应用程序重复使用一个现有的数据库连接&#xff0c;而不是重新建立一个 释放空闲时间超过最大空闲时间的连接&#xff0c;来避免因为没有释…

【硬件外设使用】——UART

【硬件外设使用】——UART UART基本概念UART通信协议UART使用方法pyb.uartmachine.uart UART 可用的传感器 UART基本概念 UART全称为Universal Asynchronous Receiver/Transmitter&#xff0c;是通过异步&#xff08;Asynchronous&#xff09;方式传输数据的一个串行通信协议。…

C6678开发概述与Sys/bios基本使用

C6678开发概述 参考开发环境标记及术语创建sys/bios自定义平台运行第一个sys/bios程序Clock模块使用Demo 参考 TMS320C6678 Multicore Fixed and Floating-Point Digital Signal Processor Datasheet TMS320C66x DSP CorePac User Guide 官方手册 创龙6678开发教程 开发环境 …

使用 ChatGPT 改善 Android 开发效率的 7 个案例~

翻译 修改自 https://proandroiddev.com/chatgpt-for-android-developers-1c3c1ecc6440&#xff0c;原作者&#xff1a;Rafa Araujo ChatGPT 是由 OpenAI 公司创造的自然语言处理工具&#xff0c;对那些想要提高技能的软件开发人员来说&#xff0c;它绝对是不容错过的重要利器…

日撸 Java 三百行day32

文章目录 说明day32 图的连通性检测1.思路1.1矩阵表示1.2.矩阵相乘1.3结合矩阵运算思考图的连通性。 2.代码 说明 闵老师的文章链接&#xff1a; 日撸 Java 三百行&#xff08;总述&#xff09;_minfanphd的博客-CSDN博客 自己也把手敲的代码放在了github上维护&#xff1a;ht…

Linux下安装navicat

1.在https://www.navicat.com.cn/download/navicat-premium下载navicat安装包 2.在终端执行命令 给navicat16-premium-cs.AppImage赋予可执行的权限 chmod x navicat16-premium-cs.AppImage 启动Navicat16 ./navicat16-premium-cs.AppImage 3.点击连接——mysql——输入连…

java单机秒杀扛1万并发方案和代码

我们先来看普通的加锁加事务秒杀性能, 说明: 1.这里的秒杀业务执行一次耗时100毫秒 2.电脑配置16g内存 4核8线程 cpu i7 7代,数据库连接池max20 RequestMapping("/purchase2")public ResultJson purchase2( Long productId){int userId new Random().nextInt(10…

2 常见模块库(2)

2.5 复用器与分路器模块 Mux是一种用于将多个信号组合成一个信号的模块。Mux模块的名称来源于多路复用器&#xff08;Multiplexer&#xff09;。 使用Mux可以将多个输入信号组合成一个向量或矩阵&#xff0c;以便在模型中传递和处理。Mux模块可以接受任意数量的输入信号&#x…

Visio Studio 2017利用Qt插件开发Qt应用的安装方法

Visio Studio 2017利用Qt插件开发Qt应用的安装方法 1 安装Visio Studio 20172 安装QT3 在Visio Studio 2017中安装Qt插件 本教程介绍如何利用Visio Studio 2017&#xff0c;开发Qt.5.14.2的Qt应用 1 安装Visio Studio 2017 链接&#xff1a;https://pan.baidu.com/s/1t9j1fFj3…

Linux --- 简介、安装

一、Linux简介 1.1、主流操作系统 不同领域的主流操作系统&#xff0c;主要分为以下这么几类&#xff1a; 桌面操作系统、服务器操作系统、移动设备操作 系统、嵌入式操作系统。接下来&#xff0c;这几个领域中&#xff0c;代表性的操作系统是那些? 1、桌面操作系统 2、服务…

2023年农牧行业数字化:7大CRM软件、5大场景盘点

目录 一、5大业务场景能力&#xff0c;解密农牧行业持续增长秘籍 1、营销获客 2、客户管理 3、商机管理 4、生态“互联”能力 5、业财一体化 二、农牧行业企业CRM选型指南 1、SaaS模式或私有部署 2、是否具有行业成功“经验” 3、可扩展性 4、以营销为主题的体系建设…

【MySQL】基础介绍及表操作

目录 1.MySQL是什么&#xff1f; 2.为什么要学习数据库呢&#xff1f; 内存和硬盘的区别 3.数据库基本操作 1.创建数据库 2.使用数据库 3.删除数据库&#xff08;慎用&#xff09; 4.查看警告信息 5.查询当前数据可服务使用的编码集 7.表操作 1.创建一个学生成绩表 2…

Spring Cloud微服务网关Zuul过滤链和整合OAuth2+JWT入门实战

一、Spring Cloud Zuul 过滤链 1.1 工作原理 Zuul的核心逻辑是由一系列的Filter来实现的&#xff0c;他们能够在进行HTTP请求或者相应的时候执行相关操作。Zuul Filter的主要特性有一下几点&#xff1a; Filter的类型&#xff1a;Filter的类型决定了它在Filter链中的执行顺序…

解决Ubunt20.04安装Sogou输入法失败进不去桌面 及 中文输入法安装

目录解决Ubunt20.04安装Sogou输入法失败进不去桌面中文输入法安装解决wps无法输入中文解决Ubunt20.04安装Sogou输入法失败进不去桌面 问题&#xff1a; Ubuntu20.04 安装了 fcitx 和 sogou 输入法&#xff1b;键盘输入法系统由 IBus 改成了 fcitx&#xff1b;重启后可以出现登…

文心一言 VS chatgpt (11)-- 算法导论2.3 7题 3.1 4~5题

四、2 ^ (n1)O(2 ^ n)成立吗? 2^(2n)O(2 ^ n)成立吗? 文心一言&#xff1a; chatgpt&#xff1a; 第一个式子不成立&#xff0c;第二个式子成立。 2 ^ (n1) O(2 ^ n) 该式不成立。按照大O符号的定义&#xff0c;如果存在正常数c和n0&#xff0c;使得n>n0时&#xf…