初始JavaEE篇 —— 网络编程(2):了解套接字,从0到1实现回显服务器

news2024/11/17 17:55:14

 找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程程(ಥ_ಥ)-CSDN博客

所属专栏:JavaEE

目录

 TCP 与 UDP

Socket套接字

UDP

TCP


网络基础知识   在一篇文章中,我们了解了基础的网络知识,网络的出现就是为了不同机器之间进行通信从而实现资源共享。现如今我们使用网络进行的一系列操作,打游戏、网上购物、网上聊天等都是客户端与服务器之间通信,准确的来说是多个客户端之间通过服务器这个平台来实现通信。而今天我们就是要来实现一个最简单的服务器与客户端。在此之前还得了解一些基本概念。

 TCP 与 UDP

上文了解了 TCP/IP 五层协议的基本分层,在以后的日常开发中,写的一些应用程序都是工作在应用层,而应用层是基于传输层的,我们也是需要了解传输层的传输协议的,主要是两个协议:TCP协议 与 UDP 协议。 

TCP 是 有连接、可靠传输、面向字节流、全双工。

UDP 是 无连接、不可靠传输、面向数据报、全双工。

连接:是指通信双方是否会保存对方的信息。有连接就说明,通信的双方会保存对方的信息。

可靠传输:由于数据在经过封装之后,是通过网卡将二进制的数据传输给另一方的,这里的二进制是通过电信号或者光信号传播的,而这种传播方式肯定是会收到外界的影响,例如,太阳爆发耀斑等情况就会影响数据的传输。因此数据传输的过程中可能会失败,如果传输失败之后,有提醒重新传输的话,这就是可靠传输,反之,传输之后不管不顾了,这就是不可靠传输。

面向字节流与面向数据报是指两者的数据传输的方式不一样,虽然最终通过网卡出去的数据都是二进制的,但是在通过传输层时,会根据协议的不同,而选择不同的方式。使用UDP传输时,就需要将数据封装成数据包的形式继续传给下一层。

全双工:是指数据既可以从一方传向另一方,也可以从另一方传向这一方,也就是和车流量一样,既有从左到右的车流,也有从右到左的车流。与之相反的一个名词是:半双工,这个就和管中的水流一样,只能从一方流向另一方,而不能从同时有两个方向的水流。

了解了TCP 与 UDP 的基本点之后,还需要了解 JVM对于操作系统提供的API封装后的结果,毕竟我们通过Java代码来编写网络编程时,是直接使用Java标准库中提供的类。

Socket套接字

Socket套接字,是由操作系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。 基于Socket套接字的网络程序开发就是网络编程。而经过JVM封装之后,就主要是针对 TCP 和 UDP 的。

UDP

java中使用UDP协议通信,主要基于DatagramSocket 类来创建数据报套接字,并使用
DatagramPacket作为发送或接收的UDP数据报。

因为操作系统为了方面更好的管理系统资源(包括硬件资源),所以操作系统采用了文件管理的方式来管理这些资源,这也就意味着某个应用程序去使用这些资源时,就和使用文件资源没什么区别了,也就是打开文件、使用文件、关闭文件。因此网卡资源的使用也是如此。

1、打开网卡资源

2、进行读写操作

3、关闭网卡资源

下面就来学习相关方法:

DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。

构造方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口 (一般用于客户端)
DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口 (一般用于服务器)
普通方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发送)
void close()关闭此数据报套接字

这里的数据报套接字我们可以简单的理解为网卡资源,receive方法就是通过网卡接收数据,send方法就是通过网卡发送数据。 构造方法是在打开网卡资源,close方法就是在关闭网卡资源。

DatagramPacket是UDP Socket发送和接收的数据报。

注意区分上述两个概念:DatagramSocket 是用来传送与接收数据报的,而DatagramPacket 是数据报本身的一层封装,简单理解就是数据报本身。生活中的例子,就是DatagramSocket 是属于快递站,而DatagramPacket 是属于包裹。包裹要通过快递站的分拣传递出去。

构造方法说明
DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度 (第二个参数length)
DatagramPacket(byte[] buf, int offset, int length,SocketAddress address) 构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从offset到指定长
度(第二个参数length)。address指定目的主机的IP
和端口号
具体方法说明
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;或从
发送的数据报中,获取接收端主机端口号
byte[] getData()获取数据报中的数据

 由于UDP是无连接的,因此构造UDP发送的数据报时,需要传入 SocketAddress ,该对象可以使用 InetSocketAddresS 来创建。即 InetSocketAddress 是 SocketAddress 的子类。

构造方法说明
InetSocketAddress(InetAddress addr, int port)创建一个Socket地址,包含IP地址和端口号

先来理解服务器与客户端这个两个名词的含义: 

举个例子:我们去学校食堂吃饭时,可能某个窗口的饭菜比较好吃,那么我们下一次或者以后都有可能会去这个窗口吃饭,而这个窗口肯定是一直在这个食堂的某个固定地点的,而这个窗口所服务的学生不是固定的,每个学生去吃饭时,肯定也是随机选择的座位坐下来吃饭。

针对上面的情况,食堂的窗口就是服务器,吃饭的学生就是客户端,客户端会给服务器提供请求(我们会把吃的菜告诉食堂阿姨),服务器会给客户端提供响应(食堂阿姨就会给我们打对应的菜)。因为服务器(食堂窗口)是需要给多个客户端提供响应,如果这个服务器的端口老是发生变化(窗口老是发生变化),那肯定是不方便客户端去访问的,因此服务器的IP与端口都是在一段时间内固定的,而客户端的端口(学生在吃饭找的座位)肯定是随机的,如果某个学生没在这里,但是他占了一个位置,那么肯定是不合理的,同样某个客户端没有启动进程访问服务器时,一直把端口号给踹在怀里肯定也是会对别的进程造成影响的(端口号是有限的)。

有了以上信息,我们就可以来写一个最简单服务器:回显服务器(接收到的请求就是响应,即接收的请求是什么,服务器返回的响应也就是什么,类似于鹦鹉学舌)。

服务器的处理逻辑:1、接收请求并解析;2、根据请求计算响应;3、将相应发送给响应的客户端;4、打印日志。

public class UdpEchoServer {
    // 创建网卡资源
    DatagramSocket socket = null;
    public UdpEchoServer(int port) throws SocketException {
        // 指定本机的一个固定端口号为服务器的端口号
        socket = new DatagramSocket(port);
    }
    // 启动服务器方法
    public void start() throws IOException {
        // 由于服务器是7*24小时的工作制,因此得死循环
        System.out.println("服务器启动成功~");
        while (true) {
            // 1、网卡接收请求并解析
            // 创建一个数据报来接收请求的具体内容
            // 数据报其实就是一个用来存储数据的包裹:字节数组+长度组成
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket); // 将得到的数据存储在数据报的字节数组中
            // 将数据报中的内容转成字符串为后续处理做准备
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength()); // 数据的有效长度
            // 2、根据请求计算响应
            String response = process(request);
            // 3、将响应返回给客户端
            // 也是通过数据报的形式
            // 由于UDP是无连接的,因此我们得手动去设置发送的IP与端口号
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), 0, response.getBytes().length,
                    requestPacket.getAddress(), requestPacket.getPort());
            socket.send(responsePacket);
            // 4、打印日志:客户端IP、客户端端口号、请求、响应
            System.out.printf("[%s %d]  request:%s  response:%s\n", requestPacket.getAddress(),
                    requestPacket.getPort(), request, response);
        }
    }

    // 后续如果要修改服务器的功能,就只需要重载process方法即可
    private String process(String request) {
        return request; // 回显服务器的功能
    }

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

有了服务器之后,就可以来创建客户端程序了。

public class UdpEchoClient {
    DatagramSocket socket = null;
    // UDP是不连接,因此客户端得保存对应服务器的IP与端口号
    private String serverIP = null;
    private int serverPort = 0;

    // 指定需要访问的服务器IP与端口号
    public UdpEchoClient(String serverIP, int serverPort) {
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }

    public void start() throws IOException {
        System.out.println("客户端启动成功(exit退出)~");
        // 创建网卡资源
        socket = new DatagramSocket();
        while (true) {
            // 1、开始接收用户的输入
            Scanner scanner = new Scanner(System.in);
            String request = scanner.nextLine();
            if (request.equals("exit")) {
                socket.close(); // 释放网卡资源
                System.out.println("客户端成功退出~")
                break;
            }
            // 2、将输入数据打包成数据报 (指定服务器IP与端口号)
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), 0 ,request.getBytes().length
            , InetAddress.getByName(serverIP), serverPort);
            // 3、然后再给到服务器
            socket.send(requestPacket);
            // 4、接收服务器的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            // 5、处理响应:打印响应的结果
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength()); // 有效的长度
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        // 指定对应服务器的IP与端口号
        UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
        client.start();
    }
}

注意:127.0.0.1 这就是代指当前机器的IP。 

运行结果:

客户端:

服务器:

由上图可知,客户端的运行与否和服务器没什么关系,服务器在正常运行的情况下会一直记录客户端的访问信息。 

下面就来使用另外一种协议来实现回显服务器:

TCP

ServerSocket 是创建TCP的服务器Socket的APl。

ServerSocket :

构造方法说明
ServerSocket(int port)创建一个服务器流套接字Socket,并绑定到指定端口
具体方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端
连接后,返回一个服务端Socket对象,并基于该
Socket建立与客户端的连接,否则阻塞等待
void close()关闭此套接字

Socket:

Socket是客户端Socket,或服务器中接收到客户端建立连接(accept方法)的请求后,返回的服务器Socket。不管是客户端还是服务器Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

构造方法说明
Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

 这里的构造方法有很多,但是常用的就是通过 String类型的host 来建立连接的。

具体方法说明
InetAddress getlnetAddress()返回套接字所连接的地址(对端)
int getPort()返回套接字所连接的端口号(对端)
InputStream getlnputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

对端:这个概念是相对的,站在服务器的角度,对端是指客户端;站在客户端的角度,对端指的是服务器。当然,也是可以获取本地程序的地址和端口号的, 使用的是 getLocalPort ,站在服务器的角度,获取的就是服务器自己所在端口。

这里的ServerSocket 可以理解为网卡资源,而Socket 就是保存TCP连接双方的连接。服务器的连接有很多个,因此我们需要为其申请网卡资源来随时获取新的连接。而客户端只需要和服务器连接即可,因此只需要去尝试申请对应的IP地址与端口号进行连接即可。

总体的实现思路还是和上面的UDP差不多,但是具体的实现方式有不同。

服务器:

public class TcpEchoServer {
    private ServerSocket serverSocket;

    public TcpEchoServer(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动成功~");
        while (true) {
            System.out.println("等待客户端连接...");
            Socket socket = serverSocket.accept();
            System.out.println("客户端连接成功:" + socket.getInetAddress() + ":" + socket.getPort());
            
            // 处理客户端连接,进入通信过程
            handleClient(socket);
        }
    }

    private void handleClient(Socket socket) throws IOException {
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) {

            while (true) {
                byte[] buffer = new byte[4096];
                int len = 0;

                StringBuilder sb = new StringBuilder();
                // 循环读取客户端请求并响应
                while ((len = inputStream.read(buffer)) != -1) {
                    sb.append(new String(buffer, 0, len));
                    if (sb.toString().contains("\n")) {
                        // 检测到换行符,认为请求结束
                        break;
                    }
                }
                String request = sb.toString();

                // 根据请求计算响应
                String response = process(request);

                // 先判断连接是否终止了
                if (socket.isClosed()) {
                    return;
                }
                // 将响应返回给客户端
                outputStream.write(response.getBytes());
                outputStream.flush(); // 刷新缓冲区
            }
        } finally {
            socket.close();
            System.out.println("客户端已断开连接");
        }
    }

    private String process(String request) {
        return request+"\n";
    }

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

客户端:

public class TcpEchoClient {
    private String serverIp;
    private int serverPort;

    public TcpEchoClient(String serverIp, int serverPort) {
        this.serverIp = serverIp;
        this.serverPort = serverPort;
    }

    public void start() throws IOException {
        try (Socket socket = new Socket(serverIp, serverPort);
             OutputStream outputStream = socket.getOutputStream();
             InputStream inputStream = socket.getInputStream();
             Scanner scanner = new Scanner(System.in)) {

            System.out.println("客户端连接服务器成功~");

            // 循环发送请求并接收响应
            while (true) {
                System.out.print("输入请求数据(exit退出): ");
                // 加上换行符,让服务器在读取数据时,知道这个是结束的标志
                String request = scanner.nextLine()+"\n";

                if (request.equals("exit\n")) { // 因为手动加上了换行符,因此判断也要加上
                    System.out.println("客户端请求断开连接");
                    break;
                }

                // 发送请求数据
                outputStream.write(request.getBytes());
                outputStream.flush(); // 刷新缓冲区,更好地让数据发送

                // 接收服务器响应
                byte[] buffer = new byte[4096];
                StringBuilder responseBuilder = new StringBuilder();
                int len = 0;
                
                // 使用while循环读取直到服务器停止发送
                while ((len = inputStream.read(buffer)) != -1) {
                    responseBuilder.append(new String(buffer, 0, len));
                    if (responseBuilder.toString().contains("\n")) { // 检测到换行符,认为响应完整
                        break;
                    }
                }
                
                // 打印完整的响应
                String response = responseBuilder.toString();
                System.out.print("接收到服务器响应: " + response);
            }
        }
    }

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

注意:

1、 因为TCP是字节流,因此我们使用的是前面文件IO操作的字节流来进行发送与读取数据。但方式略微不同,我们需使用连接获取字节输入流与字节输出流。

2、由于这里的输入输出流是建立在连接之上的,我们不知道什么时候输入与输出结束,因此我们得手动地去设置结束标志或者使用socket的shoudownOutput,后者不推荐使用,后者是直接关闭了输出流,从而导致连接中断,可能会影响后续程序逻辑的执行,而前者是我们手动地去使用标记符来判断,这样的处理更好。

3、对于资源的关闭,也应该即使去做,这里是Socket、InputStream、OutputStream等资源都需要我们手动地去关闭,防止造成资源泄露,特别是Socket资源,可能会有非常多个客户端要建立连接,但是资源有限,因此会阻塞等待后面的,如果不释放的话,就导致后续客户端无法申请到。

上述代码虽然能够达到基本的运行效果,但是还存在部分缺陷(TCP的代码):

1、同一时刻只能有一个客户端去执行服务器的逻辑,因为我们在处理请求时,也是使用的一个循环,因此这里就会导致服务器的逻辑卡在了处理请求的代码中,而不会去尝试建立新的连接。

解决方法:多线程。将处理请求的代码放到一个新的线程中,这样后续的客户端都只会占用别的线程,而不会占用main线程。

2、在引入多线程的基础上,又有一个新的问题来了:如果客户端的请求非常简单(回显这种),且同一时刻有非常多的客户端去申请服务器为其服务的话,这时候就会出现线程频繁地创建与删除,这就会导致服务器的性能比较低,因此我们可以创建一个线程池来解决上述问题。

以上就是使用TCP与UDP实现网络通信的基本过程,后面我们在学习TCP与UDP的通信保障与具体实现等。

好啦!本期 初始JavaEE篇 —— 网络编程(2):了解套接字,从0到1实现回显服务器 的学习之旅就到此结束啦!我们下一期再一起学习吧!

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

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

相关文章

机器学习 AdaBoost 算法

AdaBoost 提升学习算法是通过训练多个弱分类算法实现一个强分类算法,做法非常朴素,在训练过程中,提供分类错误的数据权重,降低分类正确的权重,提高分类效果好的弱分类器权重,降低分类效果差的若分类器权重。…

C++编程技巧与规范-类和对象

类和对象 1. 静态对象的探讨与全局对象的构造顺序 静态对象的探讨 类中的静态成员变量(类类型静态成员) 类中静态变量的声明与定义&#xff08;类中声明类外定义&#xff09; #include<iostream> using namespace std;namespace _nmspl {class A{public:A():m_i(5){…

golang中的init函数

程序的初始化和执行都起始于 main 包。如果 main 包还导入了其它的包&#xff0c;那么就会在编译时将它们依次 导入。有时一个包会被多个包同时导入&#xff0c;那么它只会被导入一次&#xff08;例如很多包可能都会用到 fmt 包&#xff0c;但 它只会被导入一次&#x…

【大数据学习 | flume】flume之常见的sink组件

Flume Sink取出Channel中的数据&#xff0c;进行相应的存储文件系统&#xff0c;数据库&#xff0c;或者提交到远程服务器。Flume也提供了各种sink的实现&#xff0c;包括HDFS sink、Logger sink、Avro sink、File Roll sink、HBase sink&#xff0c;。 ​ Flume Sink在设置存…

数学分组求偶数和

问题描述 小M面对一组从 1 到 9 的数字&#xff0c;这些数字被分成多个小组&#xff0c;并从每个小组中选择一个数字组成一个新的数。目标是使得这个新数的各位数字之和为偶数。任务是计算出有多少种不同的分组和选择方法可以达到这一目标。 numbers: 一个由多个整数字符串组…

构建安全护盾:HarmonyOS 应用的数据安全审计与日志管理实战

文章目录 前言数据安全审计与日志管理的重要性什么是数据安全审计&#xff1f;为什么需要日志管理&#xff1f; 数据安全审计与日志管理的基本原则实现数据安全审计与日志管理的技术方案1. 数据安全审计的实现2. 日志管理的实现 ArkUI 与 ArkTS 的代码示例1. 审计日志记录2. 实…

ReactPress与WordPress:两大开源发布平台的对比与选择

ReactPress与WordPress&#xff1a;两大开源发布平台的对比与选择 在当今数字化时代&#xff0c;内容管理系统&#xff08;CMS&#xff09;已成为各类网站和应用的核心组成部分。两款备受欢迎的开源发布平台——ReactPress和WordPress&#xff0c;各自拥有独特的优势和特点&am…

HarmonyOS 开发环境搭建

HarmonyOS&#xff08;鸿蒙操作系统&#xff09;作为一种面向全场景多设备的智能操作系统&#xff0c;正逐渐在市场上崭露头角。为了进入HarmonyOS生态&#xff0c;开发者需要搭建一个高效的开发环境。本文将详细介绍如何搭建HarmonyOS开发环境&#xff0c;特别是如何安装和配置…

基于VUE实现语音通话:边录边转发送语言消息、 播放pcm 音频

文章目录 引言I 音频协议音频格式:音频协议:II 实现协议创建ws对象初始化边录边转发送语言消息 setupPCM按下通话按钮时开始讲话,松开后停止讲话播放pcm 音频III 第三库recorderplayer调试引言 需求:电台通讯网(电台远程遥控软件-超短波)该系统通过网络、超短波终端等无线…

【提高篇】3.3 GPIO(三,工作模式详解 上)

目录 一,工作模式介绍 二,输入浮空 2.1 输入浮空简介 2.2 输入浮空特点 2.3 按键检测示例 2.4 高阻态 三,输入上拉 3.1 输入上拉简介 3.2 输入上拉的特点 3.3 按键检测示例 四,输入下拉 4.1 输入下拉简介 4.2 输入下拉特点 4.3 按键检测示例 一,工作模式介绍…

微服务瞎写

1.微服务解决的问题 1、如何发现新节点以及检查各节点的运行状态&#xff1f; 2、如何发现服务及负载均衡如何实现&#xff1f; 3、服务间如何进行消息通信&#xff1f; 4、如何对使用者暴露服务API&#xff1f; 5、如何集中管理各节点配置文件&#xff1f; 6、如何收集各…

Python Tornado框架教程:高性能Web框架的全面解析

Python Tornado框架教程&#xff1a;高性能Web框架的全面解析 引言 在现代Web开发中&#xff0c;选择合适的框架至关重要。Python的Tornado框架因其高性能和非阻塞I/O特性而备受青睐。它特别适合处理大量并发连接的应用&#xff0c;比如聊天应用、实时数据处理和WebSocket服务…

基于图像分类的对抗攻击算法研究

图像分类与对抗攻击 图像分类是计算机视觉的基础任务&#xff0c;旨在 将不同类别的图像准确归类 。随着深度学习发展&#xff0c;模型在大规模数据集上的表现已超越人类。然而&#xff0c;这一进步也引发了新的安全挑战—— 对抗攻击 。 对抗攻击通过向原始图像添加精心设计的…

【AI大模型】ELMo模型介绍:深度理解语言模型的嵌入艺术

学习目标 了解什么是ELMo.掌握ELMo的架构.掌握ELMo的预训练任务.了解ELMo的效果和成绩.了解ELMo的优缺点. 目录 &#x1f354; ELMo简介 &#x1f354; ELMo的架构 2.1 总体架构 2.2 Embedding模块 2.3 两部分的双层LSTM模块 2.4 词向量表征模块 &#x1f354; ELMo的预…

Linux Android 正点原子RK3568替换开机Logo完整教程

0.这CSDN是有BUG吗?大家注意:表示路径的2个点号全都变成3个点号啦! 接下来的后文中,应该是2个点都被CSDN变成了3个点: 1.将这两个 bmp 图片文件720x1280_8bit拷贝到内核源码目录下,替换内核源码中默认的 logo 图片。注意:此时还缺少电量显示图片 2.编译内核 make d…

【EasyExcel】复杂导出操作-自定义颜色样式等(版本3.1.x)

文章目录 前言一、自定义拦截器二、自定义操作1.自定义颜色2.合并单元格 三、复杂操作示例1.实体(使用了注解式样式)&#xff1a;2.自定义拦截器3.代码4.最终效果 前言 本文简单介绍阿里的EasyExcel的复杂导出操作&#xff0c;包括自定义样式&#xff0c;根据数据合并单元格等。…

Linux(CentOS)安装达梦数据库 dm8

CentOS版本&#xff1a;CentOS 7 达梦数据库版本&#xff1a;dm8 一、获取 dm8 安装文件 1、下载安装文件 打开达梦官网&#xff1a;https://www.dameng.com/ 下载的文件 解压后的文件 2、上传安装文件到 CentOS 使用FinalShell远程登录工具&#xff0c;并且使用 root 用户…

fastapi 调用ollama之下的sqlcoder模式进行对话操作数据库

from fastapi import FastAPI, HTTPException, Request from pydantic import BaseModel import ollama import mysql.connector from mysql.connector.cursor import MySQLCursor import jsonapp FastAPI()# 数据库连接配置 DB_CONFIG {"database": "web&quo…

如何监控Kafka消费者的性能指标?

要监控 Kafka 消费者性能指标&#xff0c;可以遵循以下最佳实践和策略&#xff1a; 关键性能指标监控&#xff1a; 消息吞吐量&#xff1a;监控消费者和生产者的吞吐量&#xff0c;以评估数据处理和消费的效率。延迟&#xff1a;监控端到端的延迟&#xff0c;例如通过比较消息产…

【LINUX相关】

一、Linux怎么进行查看日志&#xff1f; 首先得问问开发项目日志存放在哪里&#xff0c;可以使用多种命令来查看日志。常用的命令包括tail、cat、less和grep等。例如:1、使用tail命令可以实时查看日志文件的最新内容&#xff1a;tail -f log_file&#xff0c; 2、使用cat命令可…