【计算机网络】—网络编程(socket)02

news2024/11/24 20:44:12

目录

一、网络编程的概念

二、UDP数据报套接字编程

2.1 回显服务器代码

 2.2 翻译程序(英译汉)

三、TCP数据报套接字编程

3.1回显服务器

3.2 翻译服务器


一、网络编程的概念

网络编程:指网络上的主机,通过不同的进程,以编程的方式实现网络通信(网络数据传输)

发送端和接收端只是相对的,只是一次网络数据传输产生数据流向后的概念

Socket套接字:由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。操作系统给应用程序提供的一组API。

TCP的特点:

  • 有连接:需要连接成功才能发送数据(类似打电话)
  • 可靠传输:发送方知道接收方有没有收到数据(显示已读未读)
  • 面向字节流:以字节为单位传输(类似文件操作中的字节流)
  • 有接收缓冲区,也有发送缓冲区:
  • 全双工:一条通道,双向通信
  • 大小不限

UDP的特点:

  • 无连接 :不需要接通,直接发数据(类似发微信)
  • 不可靠传输:发送方不知道数据是否已被对方接收到
  • 面向数据报:以数据报为单位传输()
  • 有接收缓冲区,无发送缓冲区:
  • 大小受限:一次最多传输64k
  • 全双工:一条通道,双向通信

二、UDP数据报套接字编程

UDP Socket 主要涉及两个类:DatagramSocket、DatagramPacket

DatagramSocket对象,对应到操作系统的一个socket文件(文件除了普通文件,还包括硬件设备 / 软件资源)

socket文件对应“网卡”这种硬件设备,从socket文件读写数据,就是读写网卡中的信息


2.1 回显服务器代码

构造一个回显服务器(请求和响应相同):包括客户端和服务器的代码

(1)服务器端代码:

构造函数中关于port的指定:服务器需要手动指定端口号port(客户端需要根据端口号访问服务器),而客户端会自动指定端口(不知道客户端已经被占用了哪些端口),由于客户端为主动发起请求的一方,因此需要知道服务器的地址+端口

1. 启动服务器: start函数

2.读取客户端发来的请求,并将其解析为一个字符串

3.根据请求计算响应(是最复杂的过程):String response = process(request)

4.把响应写回到客户端(需要先把数据构造成一个数据报) 

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;


/*回显服务端的代码:
1.先指定端口号接收,便于指定进程处理该信息
2.启动服务器:读取请求(若读到,则需要返回)----->根据请求计算响应
 */

public class UdpEchoServer {

    private DatagramSocket socket = null;  //创建实例socket,进行网络编程的前提
    public UdpEchoServer(int port) throws SocketException {    //构造方法传入端口进行绑定
        socket = new DatagramSocket(port);
    }
// 1. 启动服务器
    public void start() throws IOException {
        System.out.println("启动服务器!");
        while (true) {
//DatagramPacket把一个字节数组(byte[])进行包装
// 2.读取客户端发来的请求,并将其解析为一个字符串
            DatagramPacket requestPacket = new DatagramPacket(new byte[1024], 1024);
            socket.receive(requestPacket);   //接收数据(一个UDP数据报)
            String request = new String(requestPacket.getData(),0,requestPacket.getLength(),"UTF-8");
//3. 根据请求计算相应(是最复杂的过程)
            String response = process(request);
//4.把响应写回到客户端(需要先把响应 数据构造成一个数据报),同时指定IP和端口
// response.getBytes()获取响应字节数组,response.getBytes().length获取字节数组长度;requestPacket.getSocketAddress():指定发回去的地址(也即是客户端)
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            //打印日志 %s:打印字符串    %d:打印有符号位十进制整数
            String log = String.format("[%s:%d] req: %s; resp: %s",
                    requestPacket.getSocketAddress().toString(),
                    requestPacket.getPort(),
                    request,response);
            System.out.println(log);

        }
    }
    //由于是回响服务器,响应和请求是一样的
    public String process(String request) {
        return request;    //返回请求值
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();//启动服务器
    }
}

(2)客户端代码:

1.从控制台读取用户输入的信息

2. 把读取的内容构造成一个UDP请求,并发送

3.从服务器读取响应数据,并解析

4.把响应结果显示到控制台

import javax.sound.sampled.Port;
import java.io.IOException;
import java.net.*;
import java.util.Scanner;

/*服务器需要手动指定端口号,而客户端会自动指定端口,无需手动执行
* */
public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;
    public UdpEchoClient(String ip,int port) throws SocketException {   //不需要指定端口port
        socket = new DatagramSocket();
        serverIP = ip;
        serverPort = port;
    }

    public void start() throws IOException {
        Scanner in = new Scanner((System.in));
        while (true) {
           // 1.从控制台读取用户输入的信息
            System.out.println("->");
            String request = in.next();
           // 2. 把读取的内容构造成一个UDP请求(String数据内容+目的地服务器地址),并发送
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serverIP),serverPort);  //等价于InetSocketAddress,获取地址和IP
            socket.send(requestPacket);
           // 3.从服务器读取响应数据,并解析
            DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(),0,responsePacket.getLength(),"utf-8");
           // 4.把响应结果显示到控制台
            System.out.printf("req: %s, resp: %s\n", request, response);
        }
    }
    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);   //因为此处服务器在本机,所以指定的IP为本机环回IP
        client.start();
    }
}

 开启多个客户端发送数据,按照下图进行配置

服务器输出结果:

 

 2.2 翻译程序(英译汉)

 (1)客户端程序不变,如上

(2)只需调整服务器代码,只要是修改process方法(根据请求处理响应)

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;

public class UdpDictServer extends UdpEchoServer{
    private HashMap<String,String> dict = new HashMap();
    public UdpDictServer(int port) throws SocketException {  //插入构造方法
        super(port);
        dict.put("cat","小猫");
        dict.put("dog","小狗");
    }
    // 重写process
    @Override    //注意重写的UdpEchoServer中的process方法不能是私有的
    public String process(String request) {
        return dict.getOrDefault(request,"该词无法被查询到");
    }

    public static void main(String[] args) throws IOException {
        UdpDictServer server = new UdpDictServer(9090);
        server.start();
    }
}

三、TCP数据报套接字编程

  • ServerSocket专门给TCP服务器使用
  • Socket给服务器和客户端使用

3.1回显服务器

1.读取请求

2.根据请求计算响应

3.把响应返回客户端

(1)服务器代码

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class TcpEpochServer {
    private ServerSocket serverSocket = null;   定义一个变量表示服务器    listen 原意为监听
    public TcpEpochServer(int port) throws IOException {   //服务器需要指定端口号
        serverSocket =  new ServerSocket(port);    //给服务器指定端口号
    }

    public void start() throws IOException {
        System.out.println("启动服务器!!");
        while (true) {
   //1.由于tcp是有连接的,需要先进行连接才能读数据  若无客户端与其建立连接,accept则会阻塞
            Socket clientSocket = serverSocket.accept();   //clientSocket完成后续操作
            processConnection(clientSocket);
        }
    }
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端建立连接", clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try (InputStream inputStream = clientSocket.getInputStream()){
            try (OutputStream outputStream = clientSocket.getOutputStream()) {
                Scanner in = new Scanner(System.in);
                while (true) {  //循环处理每个请求
            //1.读取请求
                    if (!in.hasNext()) {
                        System.out.printf("[%s:%d] 客户端建立连接", clientSocket.getInetAddress().toString(),clientSocket.getPort());
                        break;
                    }
                    String request = in.next();
            //2.根据请求计算响应
                    String response = process(request);
            //3.把响应返回客户端
                    PrintWriter printWriter = new PrintWriter(outputStream);
                    printWriter.println(response);
                    printWriter.flush();  //刷新缓冲区
                    System.out.printf("[%s:%d] req: %s,resp:%s\n",clientSocket.getInetAddress().toString(),
                            clientSocket.getPort(),request,response);
                }
            }
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                clientSocket.close();
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEpochServer server = =new TcpEpochServer(9090);
        server.start();
    }
}

(2)客户端代码(单个连接)

1.从控制台读取字符串数据

2.根据读取的数据构造请求,并把请求发送给服务器

3.从服务器读取响应,并解析

4.把结果显示到控制台上

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class TcpEpochClient {
    private Socket socket = null;   //客户端不用指定IP,由系统自动分配即可
    public TcpEpochClient(String serverIP,int serverPort) throws IOException {
        socket = new Socket(serverIP,serverPort);  //此处的端口号和IP表示的是服务器所有的,客户端通过超找到IP和端口进行连接
    }
    public void start() throws IOException {
        System.out.println("和服务器连接成功");
        Scanner in = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream()) {
            try (OutputStream outputStream = socket.getOutputStream()) {
                while (true) {
//  1.从控制台读取字符串数据
                    System.out.print("- >");
                    String request = in.next(); //请求为控制台输入内容
//  2.根据读取的数据构造请求,并把请求发送给服务器
                    PrintWriter printWriter = new PrintWriter(outputStream);//用·PrintWriter包装outputStream
                    printWriter.println(request);  //打印请求
                    printWriter.flush(); //写入之后就需要刷新,否则服务器不能及时显示
//  3.从服务器读取响应,并解析
                    Scanner respScanner = new Scanner(inputStream);  //读取输入请求的数据流
                    String response = respScanner.next();
//  4.把结果显示到控制台上
                    System.out.printf("req: %s,resp: %s\n",request,response);
                }
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEpochClient client = new TcpEpochClient("127.0.0.1",9090);
        client.start();
    }
}

(2)客户端代码(建立多个连接)

由于服务器部分while处包含嵌套循环,导致内部循环未结束便不能进行外部循环,导致服务器不能与多个客户端建立连接 。

解决:主线程循环调用accept,有客户端连接时,则主线程创建一个新线程负责对客户端的若干请求(while循环处理请求)。由于多线程是并发执行的(宏观上是同时执行),由我们观察处理时就感觉到是各个线程独立执行,互不干扰,便就实现了多客户端连接。对如下代码进行改进即可。

改进1:每次accept成功,就创建一个线程,由其负责执行客户端的请求

    public void start() throws IOException {
        System.out.println("服务器启动!!");
        while (true) {   //此处包含两个循环(processConnection方法内部有一个循环)
            //1.由于tcp是有连接的,需要先进行连接才能读数据  若无客户端与其建立连接,accept则会阻塞
            Socket clientSocket = serverSocket.accept();   //clientSocket完成后续与客户端之间的沟通
            Thread t = new Thread(() ->{    //创建线程,使processConnection在线程内部执行
                processConnection(clientSocket);
            });
            t.start();
        }
    }

改进2:每次accept成功,就创建线程池,由其负责执行客户端的请求

    public void start() throws IOException {
        System.out.println("服务器启动!!");
        ExecutorService pool = Executors.newCachedThreadPool();  //创建线程池
        while (true) {   //此处包含两个循环(processConnection方法内部有一个循环)
            //1.由于tcp是有连接的,需要先进行连接才能读数据  若无客户端与其建立连接,accept则会阻塞
            Socket clientSocket = serverSocket.accept();   //clientSocket完成后续与客户端之间的沟通
            pool.submit(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
        }
    }

 完整代码:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;



public class TcpThreadEpochServer {
    private ServerSocket serverSocket = null;   //定义一个变量表示服务器
    public TcpThreadEpochServer(int port) throws IOException {   //服务器需要指定端口号
        serverSocket =  new ServerSocket(port);  //给服务器指定端口号
    }
    public void start() throws IOException {
        System.out.println("服务器启动!!");

        while (true) {   //此处包含两个循环(processConnection方法内部有一个循环)
            //1.由于tcp是有连接的,需要先进行连接才能读数据  若无客户端与其建立连接,accept则会阻塞
            Socket clientSocket = serverSocket.accept();   //clientSocket完成后续与客户端之间的沟通
            Thread t = new Thread(() ->{    //创建线程,使processConnection在线程内部执行
                processConnection(clientSocket);
            });
            t.start();
        }
    }
    private void processConnection(Socket clientSocket) {
        System.out.printf("[%s:%d] 客户端建立连接\n", clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try (InputStream inputStream = clientSocket.getInputStream()){
            try (OutputStream outputStream = clientSocket.getOutputStream()) {
                Scanner in = new Scanner(System.in);
                while (true) {  //循环处理每个请求
                    if (!in.hasNext()) {               //1.读取请求
                        System.out.printf("[%s:%d] 客户端断开连接\n", clientSocket.getInetAddress().toString(),clientSocket.getPort());
                        break;
                    }
                    String request = in.next();
                    String response = process(request);  //2.根据请求计算响应

                    PrintWriter printWriter = new PrintWriter(outputStream);  //3.把响应返回客户端
                    printWriter.println(response);
                    printWriter.flush();  //刷新缓冲区,可能导致客户端不能第一时间看到响应
                    System.out.printf("[%s:%d] req: %s,resp:%s\n",clientSocket.getInetAddress().toString(),
                            clientSocket.getPort(),request,response);
                }
            }
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                clientSocket.close();  //关闭操作
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpThreadEpochServer server = new TcpThreadEpochServer(9090);
        server.start();
    }
}

3.2 翻译服务器

客户端内容不做改动,只需对服务器中的process方法改动即可(实现翻译的逻辑),让翻译客户端继承自上面的客户端即可。

import java.io.IOException;
import java.util.HashMap;

public class TcpDictServer extends TcpEpochServer{  //继承之后重写process方法
    private HashMap<String,String> map = new HashMap<>();  //创建一个HashMap用于接收翻译之间的映射关系
    public TcpDictServer(int port) throws IOException {
        super(port);
        map.put("cat","猫咪");
        map.put("dog","小狗");
        map.put("pig","小猪");
        map.put("apple","苹果");
        map.put("banana","香蕉");
    }
    @Override
    public String process(String request) {
        return map.getOrDefault(request,"当前语料库不足以翻译此词条");
    }

    public static void main(String[] args) throws IOException {
        TcpDictServer server = new TcpDictServer(9090);
        server.start();
    }
}

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

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

相关文章

openlayer+ol-ext 裁剪 天地图 中国或者其他省份 范围进行展示

地图未裁剪或遮盖效果&#xff08;天地图&#xff09; 效果1.crop: 1.1裁剪天地图里面效果 参数&#xff1a; inner: true 1.2裁剪天地图外面 参数&#xff1a; inner: false 核心代码&#xff1a; let crop new Crop({feature: feature[0],inner: false,});vecLayer.addF…

【保姆级】新机器部署JDKTomcat

1、登录服务器&#xff0c;如果非root用户则切root用户 sudo su - 2、在/usr/tmp目录上传JDK、Tomcat安装包 3、将安装包移到/usr/lib目录 mv xxx /usr/lib 4、解压 & 重命名 tar -xzvf xxx mv xxx jdk、mv xxx tomcat 5、配置环境变量 vim /etc/profile JAVA_HOME/u…

引用参考文献[1,2]或者[1-3]

目录准备参考视频引用参考文献[1,2]引用参考文献[1-3]准备 word 2021 参考视频 word中同一位置引用多篇参考文献角标设置 引用参考文献[1,2] 把参考文献交叉引用到文章中&#xff0c;如下图所示 选中标签[1][2] &#xff0c;点击切换域代码&#xff1a; 然后如下图所示 …

递归经典例题 --- 青蛙跳台阶(图文详解)

目录 一、介绍 二、解题思路 介绍动态规划法 三、代码实现 一、介绍 所谓的青蛙跳台阶问题&#xff0c;就是指一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级&#xff08;最多只能跳2级&#xff09;。求该青蛙跳上一个n级的台阶总共有多少种跳法。 二、解题思路 首…

Spring核心解析—Resource与ResourceLoader接口

Resource你不得不知的事情前言Resource内容继承结构DOC解释提供的功能重要的内置Resource实现UrlResourceClassPathResourceFileSystemResourceServletContextResourceInputStreamResourceByteArrayResourceResourceLoader内容结构体系源码分析ResourcePatternResolver内容容器…

gdb调试插件的安装——gef/gdbinit/peda(记录向)

源地址&#xff1a;https://github.com/hugsy/ 根据上面的要求&#xff1a; gdb必须得8.0以上&#xff0c;python得3.6以上&#xff0c;但是一般裸机的gdb都是7.2&#xff0c;python是2.7。我们需要下载更高版本的gdb和python 先将系统自带的gdb删除&#xff1a; sudo yum r…

HMS Core手语服务荣获2022中国互联网大会“特别推荐案例”:助力建设数字社会

11月15日&#xff0c;HMS Core手语服务在2022&#xff08;第二十一届&#xff09;中国互联网大会 “互联网助力经济社会数字化转型”案例评选活动中&#xff0c;荣获“特别推荐案例”。 经过一年多的技术迭代和经验积累&#xff0c;HMS Core手语服务已与多个行业的开发者合作&a…

【算法系列】非线性最小二乘求解-直接求解法

系列文章目录 【算法系列】卡尔曼滤波算法 【算法系列】非线性最小二乘求解-直接求解法 文章目录 系列文章 文章目录 前言 算法推导 总结 前言 SLAM问题常规的解决思路有两种&#xff0c;一种是基于滤波器的状态估计&#xff0c;围绕着卡尔曼滤波展开&#xff1b;另一种则是基…

深度学习入门(三十九)计算性能——分布式训练、参数服务器(TBC)

深度学习入门&#xff08;三十九&#xff09;计算性能——分布式训练、参数服务器前言计算性能——分布式训练、参数服务器课件&#xff08;分布式训练&#xff09;分布式计算GPU机器架构计算一个小批量同步SGD性能性能的权衡实践的建议总结教材&#xff08;参数服务器&#xf…

Lazada如何申请入驻Lazmall品牌商城,会给商家带来哪些权益

Lazmall入驻标准&权益 &#xff08;1&#xff09;Lazmall申请要求:①每月至少售出1单②Seller Rating 270%③Cancellation Rate (取消率) < 2%④Ship on Time及时发货率2 90%⑤Return Rate退货率≤1%⑥店铺至少有10件在线产品⑦店铺有装修⑧店内没有OEM/NO BRAND产品 …

微服务介绍与 SpringCloud Eureka

哈喽~大家好&#xff0c;前端时间我们完结了JavaEE框架篇&#xff0c;从现在开始呢&#xff0c;开始了微服务章节了&#xff0c;这篇就来看看微服务介绍与 SpringCloud Eureka。 &#x1f947;个人主页&#xff1a;个人主页​​​​​ &#x1f948; 系列专栏&…

linux级联中断控制器的处理流程

本篇文章是通过学习韦东山老师的中断子系统相关章节而成。 这里写自定义目录标题1. 下级中断控制器的类别1.1 链式中断控制器(chained)1.2 层级中断控制器(hierarchy)2. 链式(chained)中断控制器的处理流程3. 层级(hierarchy)中断控制器的处理流程4. 处理流程对比4.1 chain的中…

linux和windwos下的磁盘扇区数据查看

目录 一、linux 二、windows 一、linux linux下一切皆文件&#xff0c;磁盘也是一种设备文件。所以我们可以直接编写 C 程序读取磁盘文件数据&#xff0c;依据扇区进行读取。 #define _GNU_SOURCE #include <stdio.h> #include <errno.h> #include <stdlib.h…

二叉树习题-小记

文章目录二叉树相同的树572. 另一棵树的子树110. 平衡二叉树101. 对称二叉树236. 二叉树的最近公共祖先JZ36 二叉搜索树与双向链表105. 从前序与中序遍历序列构造二叉树606 根据二叉树创建字符串二叉树 相同的树 相同的树 给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个…

秒杀同龄人的顶级复盘能力,坚持1年,领先5年!

什么是复盘 复盘一词起源于围棋术语&#xff0c;本意是对弈者下完一盘棋之后&#xff0c;重新把过程摆一遍&#xff0c;看哪些地方下得好&#xff0c;哪些不好&#xff0c;总结经验。 在头脑中把过去做的事情“过”一遍&#xff0c;通过对过去的思维和行为进行回顾、反思和探…

使用STM32CubeMX实现LED闪烁

需提前学习&#xff1a;STM32CubeMX新建工程并点亮一个LED&#xff1b; 目录 打开GPIO相关文件 我们需要看的部分 HAL_GPIO_ReadPin&#xff08;&#xff09; HAL_GPIO_WritePin&#xff08;&#xff09; 函数声明 GPIOx GPIO_Pin PinState HAL_GPIO_TogglePin&…

538页21万字数字政府大数据云平台项目建设方案

2 项目建设需求 2.1 项目建设目标 2.2 项目建设内容 2.3 项目建设要求 2.3.1 基础设施服务建设 2.3.2 信息安全服务建设 2.3.3 运行保障服务建设 2.3.4 业务应用支撑建设 3 平台建设方案 3.1 总体建设原则 3.2 总体方案设计 3.2.1 方案设计思路 3.2.2 总体架构设计 …

Spark RDD编程模型及算子介绍(二)

文章目录常见的Action算子常见分区操作算子常见的Action算子 countByKey算子&#xff1a;统计Key出现的次数&#xff0c;部分代码如下&#xff1a; rdd_file sc.textFile("../Data/input/words.txt") rdd_map rdd_file.flatMap(lambda line: line.split(" &…

Mybatis拦截器源码详解

Mybatis拦截器源码详解Mybatis相关全览一、简介执行与添加顺序拦截器生效入口二、使用例子三、原理加载入口生成代理遍历拦截器匹配&生成代理四、实践例子本文用的是3.5.10版本 源码地址&#xff1a;https://github.com/mybatis/mybatis-3/releases 文档地址&#xff1a;ht…

【云原生监控系列第三篇】Prometheus普罗米修斯进阶——PromQL的指标类型

目录一、PromQL 的指标类型1.1 Counter1.2 Gauge1.3 Histogram1.4 Summary1.5 Histogram 与 Summary 的异同二、Prometheus 的聚合函数三、PromQL 的聚合表达式一、PromQL 的指标类型 PromQL 有四个指标类型&#xff1a; Counter &#xff1a;计数器&#xff0c;用于保存单调递…