【网络编程】TCP

news2024/12/25 22:40:28

请添加图片描述

✨个人主页:bit me👇
✨当前专栏:Java EE初阶👇

目 录

  • 🔮一. TCP流套接字编程
  • 💿二. TCP中的长短连接
  • 📀三. 写一个 TCP 版本的 回显服务器-客户端

🔮一. TCP流套接字编程

  • ServerSocket API

ServerSocket 是创建TCP服务端Socket的API。

ServerSocket:是服务器端使用的 Socket

ServerSocket 构造方法:

方法签名方法说明
ServerSocket(int port)创建一个服务端流套接字Socket,并绑定到指定端口

ServerSocket 方法:

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

accept:没有参数,返回值是一个 Socket 对象,功能是等待有客户端和服务器建立上连接,accept 则会把这个连接获取到进程中,进一步的通过返回值的 Socket 对象来和客户端进行交互。

在此处可以举例为售楼,比如外场有男生拉客人,女生内场介绍售楼服务,其中 ServerSocket 就是外场连接,通过 accept 把连接交给了 Socket ,然后 Socket 对象和客户端进行沟通。

  • Socket API

Socket:服务器和客户端都会使用的 Socket 。

Socket 是客户端 Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端 Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端信息,及用来与对方收发数据的。

Socket 构造方法:

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

Socket 方法:

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutputStream getOutputStream()返回此套接字的输出流

第二和第三个方法的传输数据,不是直接通过 Socket 对象,而是 Socket 内部包含了输入流对象(接收)输出流对象(发送)


💿二. TCP中的长短连接

TCP发送数据时,需要先建立连接,什么时候关闭连接就决定是短连接还是长连接:

  • 短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能一次收发数据。
  • 长连接:不关闭连接,一直保持连接状态,双方不停的收发数据,即是长连接。也就是说,长连接可以多次收发数据。

对比以上长短连接,两者区别如下:

  1. 建立连接、关闭连接的耗时:短连接每次请求、响应都需要建立连接,关闭连接;而长连接只需要第一次建立连接,之后的请求、响应都可以直接传输。相对来说建立连接,关闭连接也是要耗时的,长连接效率更高。
  2. 主动发送请求不同:短连接一般是客户端主动向服务端发送请求;而长连接可以是客户端主动发送请求,也可以是服务端主动发。
  3. 两者的使用场景有不同:短连接适用于客户端请求频率不高的场景,如浏览网页等。长连接适用于客户端与服务端通信频繁的场景,如聊天室,实时游戏等。

📀三. 写一个 TCP 版本的 回显服务器-客户端

服务器启动的时候,进行 accept ,accept 进行的工作是拉客,对于操作系统来说,建立 TCP 连接,是内核工作,accept 要干的就是等连接建立好了之后把这个连接拿到应用程序中,如果当前连接还没建立,accept 就会阻塞!accept 相当于,有人给你打电话了,你按下接听键!如果没人打电话,阻塞等待有人给你真的打电话了才行。

如果服务器返回多次响应,其实这多次响应,都是以字节为单位返回给客户端的,客户端操作系统内核就会收到这些字节,然后调用 read / scanner.next 这样的方法的时候,就是从内核中读取。

  • read :如果使用字节流的 read ,就是在读取固定长度的字节。即使返回 10 次,每次返回 10 个字节,这里 read (buf[100]),这种操作一次性就读出来了,也可以多次读取
  • scanner.next :隐含的东西,这里的 next 是读到空白符(空格,回车,换行,制表,翻页,垂直制表…)

测试网络程序的时候,务必要先启动服务器,再启动客户端。

客户端代码:

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 TcpEchoClient {
    private Socket socket = null;

    public  TcpEchoClient() throws IOException {
        //new 这个对象,是要和服务器建立连接的
        //建立连接,是要知道服务器在哪里
        socket = new Socket("127.0.0.1",8000);
    }

    public void start() throws IOException {
        //由于我们建立的是长连接,一个连接会处理 N 个请求和响应
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()){
            Scanner scannerNet = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true){
                //从控制台读取用户输入
                System.out.println("> ");
                String request = scanner.next();
                //2. 把请求发送给服务器
                printWriter.write(request);
                printWriter.flush();
                //3. 从服务器读取响应
                String response = scannerNet.next();
                //4. 把结果显示到界面上
                System.out.printf("req : %s; resp : %s\n",request,response);
            }
        }
    }

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

服务器代码:

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 TcpEchoServer {
    private ServerSocket serverSocket = null;

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

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while (true){
            //如果当前没有客户端来进行连接,就会阻塞等待
            Socket clientSocket = serverSocket.accept();
            processConnect(clientSocket);
        }
    }

    //通过这个方法,给当前连上的这个客户端,提供服务
    //一个连接过来了,服务方式可能有两种
    //1. 一个连接只进行一次数据交互(一个请求 + 一个响应)  短连接
    //2. 一个连接进行多次数据交互(N 个请求 + N 个响应)   长连接
    //此处来写长连接版本
    public void processConnect(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 建立连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);

            //这里是长连接的写法,需要用循环来获取到多次交互的情况
            while (true){
                if(!scanner.hasNext()){
                    //断开连接,当客户端断开连接的时候,此时 hasNext 就会返回 false
                    System.out.printf("[%s:%d] 断开连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //1. 读取请求并解析
                String request = scanner.next();
                //2. 根据请求计算响应
                String response = process(request);
                //3. 把响应返回给客户端
                printWriter.write(response);
                // 刷新一下缓冲区,避免数据没有发出去
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),
                        request,response);
            }
        }
    }
    public String process(String req){
        return req;
    }

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

运行效果:

在这里插入图片描述
在这里插入图片描述

但是在此处我们发现客户端和服务器并没有想我们之前那样连接起来,输入内容之后,没发生反应,有两种可能性:

  1. 客户端没发出去(很可能没加 flush)
  2. 服务器收到了没有处理(很可能服务器还在阻塞的状态)

经过排查,猜测上面代码问题应该出现在服务器中,但是我们如何确认阻塞在哪里了呢?

我之前写过的,线程出现问题,可以查看 jconsole 查看当前线程状况,看到每个线程的调用栈

客户端中:

在这里插入图片描述

服务器中:

在这里插入图片描述

发现都是在 next 中阻塞了,下面两行就是代码阻塞的位置。

但是客户端阻塞在这里,是很合理的!等待服务器返回响应,服务器当前确实没有返回响应(如果服务器返回响应了,服务器会打印的,实际上服务器没打印这个日志)

next 啥时候会执行结束?就是当读到空白符空格,回车,换行,制表,翻页,垂直制表…)的时候才结束。

scanner.next 的行为是:尝试往后读,读到空白符就结束,会返回一个字符串,返回值里是不包含刚才最后的空白符的。由此可见,我们在客户端发送的信息中再加个空白符就行了。

此处会有人说可以用 nextline,nextline 是判定 /n,next 是判断空白符(包含 /n),都差不多。

所以此处最好的修改就是把 next 后面含有 write 的写法改成 println

客户端中第二步:把请求发送给服务器改为:printWriter.println(request);
服务器中第三步:把响应返回给客户端改为:printWriter.println(response);

改变后的代码执行就正确了

在这里插入图片描述
在这里插入图片描述

但是但是但是!!!bug 并没有全部解决,还有两个 bug。

  1. 上述代码中,使用 clientSocket !用完之后,是否要关闭呢?当然要关闭!和上面的 ServerSocket 不同!

生命周期不同,ServerSocket 的生命周期,是跟随整个程序的,clientSocket 的生命周期,只是当前连接!就应该在连接之后,把这里的 socket 进行关闭。如果不关闭,妥妥的资源泄露,ServerSocket 只有一个,clientSocket 会有无数个!每个客户端的连接,都是一个!!!

因此解决方案就是在 try 后面加个 finally ,finally 中写 clientSocket.close(); 即可

  1. 上述代码无法处理多个客户端

当第一个客户端连上服务器之后,服务器提示 “建立连接”
当第二个客户端也连接上的时候,服务器没提示
 
第一个客户端发的请求,可以正常处理
第二个客户端发的请求,不能处理
 
当把第一个客户端关闭之后,第二个客户端才能真正连接成功,之前发的请求,才有回应。

在服务器代码中:

在这里插入图片描述
在这里插入图片描述

当第一个客户端连接上之后,服务器就进入了 processConnect 代码,就在 while 循环 next 这里阻塞了,processConnect 方法就执行不完了,同时也就无法第二次执行到 accept ,也就无法处理第二个客户端。

next 和 循环 影响了服务器第二次调用 accept

因此解决方案就是既能够执行到 processConnect 里面的循环,又能够执行到 accept 即可,所以在这里,我们可以使用多线程。在主线程中执行 accept ,创建一个新的线程来调用 processConnect ,这样原来主线程就不会受影响。

在这里插入图片描述

执行效果:

  • 客户端1
    在这里插入图片描述

  • 客户端2
    在这里插入图片描述

  • 客户端3
    在这里插入图片描述

  • 服务器
    在这里插入图片描述

虽然比最开始单线程有提升,但是涉及到频繁创建销毁线程,在高并发的状态下,负担比较重,因此这里可以再优化使用线程池。

使用线程池,来解决频繁创建销毁线程的问题

//注意线程池在循环外面创建
ExecutorService service = Executors.newCachedThreadPool();
//循环内部
service.submit(new Runnable() {
    @Override
    public void run() {
        try {
            processConnect(clientSocket);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
});

当前是使用了线程池解决了问题,但是如果并发量太高了!就会导致池子里的线程特别特别多!就说明内存等资源调度开销特别大,,于是乎有了进一步的改进,是否能减少线程的数目?当前是一个线程对应一个客户端,能不能让一个线程对应多个客户端?当然可以,但是此处就不再拓展了,想知道的老铁可以去搜搜 “IO 多路复用”,本质上就是一个线程处理多个 socket ,操作系统提供的机制。

最后附上改进后的总代码:

服务器:

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 TcpEchoServer {
    private ServerSocket serverSocket = null;

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

    public void start() throws IOException {
        System.out.println("服务器启动!");
        ExecutorService service = Executors.newCachedThreadPool();
        while (true){
            //如果当前没有客户端来进行连接,就会阻塞等待
            Socket clientSocket = serverSocket.accept();
            //不再直接调用了,创建一个新的线程,让线程来调用他
            //版本1: 使用单线程,存在 bug ,无法处理多线程情况
            //processConnect(clientSocket);

			//版本2: 使用多线程,主线程负责拉客,新线程负责通信
			//虽然比版本 1 有提升,但是涉及到频繁创建销毁线程,在高并发的状态下,负担比较重
            //Thread t = new Thread(()->{
            //    try {
            //        processConnect(clientSocket);
            //    } catch (IOException e) {
            //        e.printStackTrace();
            //   }
            //});
            //t.start();

			//版本3: 使用线程池,来解决频繁创建销毁线程的问题
			//此处不太适合使用 "固定个数的"
			//在外面创建线程池
            service.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnect(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    //通过这个方法,给当前连上的这个客户端,提供服务
    //一个连接过来了,服务方式可能有两种
    //1. 一个连接只进行一次数据交互(一个请求 + 一个响应)  短连接
    //2. 一个连接进行多次数据交互(N 个请求 + N 个响应)   长连接
    //此处来写长连接版本
    public void processConnect(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 建立连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()){
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);

            //这里是长连接的写法,需要用循环来获取到多次交互的情况
            while (true){
                if(!scanner.hasNext()){
                    //断开连接,当客户端断开连接的时候,此时 hasNext 就会返回 false
                    System.out.printf("[%s:%d] 断开连接!\n",clientSocket.getInetAddress().toString(),clientSocket.getPort());
                    break;
                }
                //1. 读取请求并解析
                String request = scanner.next();
                //2. 根据请求计算响应
                String response = process(request);
                //3. 把响应返回给客户端
                printWriter.println(response);
                // 刷新一下缓冲区,避免数据没有发出去
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %s\n",clientSocket.getInetAddress().toString(),
                        clientSocket.getPort(),
                        request,response);
            }
        }finally {
            //加在这里是更稳妥的做法
            clientSocket.close();
        }
    }
    public String process(String req){
        return req;
    }

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

客户端:

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 TcpEchoClient {
    private Socket socket = null;

    public  TcpEchoClient() throws IOException {
        //new 这个对象,是要和服务器建立连接的
        //建立连接,是要知道服务器在哪里
        socket = new Socket("127.0.0.1",8000);
    }

    public void start() throws IOException {
        //由于我们建立的是长连接,一个连接会处理 N 个请求和响应
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()){
            Scanner scannerNet = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true){
                //从控制台读取用户输入
                System.out.println("> ");
                String request = scanner.next();
                //2. 把请求发送给服务器
                printWriter.println(request);
                printWriter.flush();
                //3. 从服务器读取响应
                String response = scannerNet.next();
                //4. 把结果显示到界面上
                System.out.printf("req : %s; resp : %s\n",request,response);
            }
        }
    }

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

TCP 版本的字典查找

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

public class TcpDictServer extends TcpEchoServer{
    private Map<String,String> dict = new HashMap<>();

    public TcpDictServer(int port) throws IOException {
        super(port);

        dict.put("cat","小猫");
        dict.put("dog","小狗");
        dict.put("fuck","卧槽");
    }

    public String process(String req){
        return dict.getOrDefault(req,"俺也不知道是啥");
    }

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

运行结果展示:

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

d2l 注意力评分函数 --附加mask_softmax讲解

本章节tensor处理操作也不少&#xff0c;逐个讲解下&#xff1a; 目录 1.mask_softmax 1.1探索源码d2l.sequence_mask 2.加性注意力 3.缩放注意力 1.mask_softmax dim-1表示对最后一个维度进行softmax .dim()返回的是维度数 对于需要mask的数&#xff0c;要用绝对值非…

FreeRTOS 任务创建与删除实验

本实验主要实现 FreeRTOS 使用动态方法创建和删除任务&#xff0c;本实验设计了四个任务&#xff0c;这四 个任务的功能如下表所示&#xff1a; 软件设计 1. 程序流程图 本实验的程序流程图&#xff0c;如下图所示&#xff1a; 2. FreeRTOS 函数解析 (1) 函数 xTaskCreate…

spring框架基础知识和基于XML的Bean对象的管理回顾

什么是spring框架&#xff1f; spring基本功能所必须的jar包就是这些 如何获取bean&#xff1f; IOC原理 上面耦合度太高了 改进使用工厂模式 上面并没有把耦合度降低到最低&#xff0c;使用反射 spring实现IOC的两种方式 BeanFactory和ApplicationContext IOC如何管理Bea…

5个方法,帮助你快速提高团队管理效率

团队中&#xff0c;大家看起来都很忙&#xff0c;但最终交付的结果却总是差强人意。会议那么多&#xff0c;但有效的却很少越管理&#xff0c;但偏偏有时候越管理越乱......相信以上这些问题&#xff0c;很多管理者都有遇到过&#xff0c;团队管理是一个项目中最关键的一环。好…

如何打造全流程数字化零工场景,实现零工管理一体化?

近年来&#xff0c;零工市场发展迅速&#xff0c;不仅为企业提供更低成本、更便捷的用工方式&#xff0c;也为劳动者就业提供更低门槛更灵活形式&#xff0c;发挥了就业「蓄水池」的重要作用。但由于零工经济模式下的用工形式非常灵活&#xff0c;企业想要管好零工并不容易。 …

短视频平台-小说推文(知乎)推广任务详情

知乎会员 知乎日结内测中&#xff0c;可能暂只对部分优质会员开放! 2023/03/29通知: 知乎拉新项目&#xff0c;由于内部测试转化较低&#xff0c;暂时下线&#xff0c;原有关键词出单不受影响。 1、关键词 1.1 选择会员文 在知乎【首页】或者【会员】里面选取&#xff0c;需…

PEIS体检系统全套源代码,C# 源码

医院体检信息系统PEIS源码,C# 源码&#xff0c;PEIS源码源码 文末获取联系&#xff01; 系统概述 医院体检信息系统是专门针对医院体检中心的日常业务运作的特点和流程&#xff0c;结合数字化医院建设要求进行设计研发的一套应用系统。该系统覆盖体检中心的所有业务&#xff0…

使用nvm替换nvmw作为nodejs的版本切换(亲测)

之前的文章&#xff1a;同时使用vue2.0和vue3.0版本的采坑记录 安装的nvmw&#xff0c;今天想要用nvmw切换时&#xff0c;居然给我报错了&#xff1a; 然后我就走上了使用nvm替换nvmw之路。。 1.安装 nvm-windows下载 下载release版 中Assets中的包&#xff0c;window10&…

APIs -- DOM正则表达式

1. 介绍 正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在JavaScript中&#xff0c;正则表达式也是对象通常用来查找、替换那些符合正则表达式的文本&#xff0c;许多语言都支持正则表达式。正则表达式在JavaScript中的使用场景: 例如验证表单:用户名表单…

数据库的实际操作

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、关系模型二、数据库的操作 创建数据库查看数据库选择数据库删除数据库三、MySQL 数据库命名规范总结 一、关系模型 关系数据库是建立在关系模型上的。而关系模…

flutter学习之旅(一)

初学Flutter flutter官网和中文开发手册 安装flutter - windows 官方文档-windows flutter_windows_3.7.9-stable.zip 编辑环境变量 在 用户变量 一栏中&#xff0c;检查是否有 Path 这个条目&#xff1a; 如果存在这个条目&#xff0c;以 ; 分隔已有的内容&#xff0c;加入 f…

物联网能源能耗之场景控制原理

物联网能源能耗系统利用物联网技术&#xff0c;可帮助企业构建能耗分布&#xff0c;帮助操作人员实时监控各类关键参数&#xff0c;计算关键环节的能耗指标&#xff0c;和既定的能耗基线进行对比&#xff0c;得出能耗差距。 对于制造企业而言&#xff0c;物联网能源能耗不仅能…

商业策划的基本功:竞品分析

商业策划的基本功&#xff1a;竞品分析 商业的三个视角&#xff1a;用户&#xff0c;竞争&#xff0c;自己 有方法会更有效 趣讲大白话&#xff1a;磨刀不误砍柴工 【趣讲信息科技138期】 **************************** 世界上如果只有一种矿泉水 就不会竞争 就不会有农夫山泉这…

由世纪互联运营的Microsoft Teams携创新功能正式发布,夯实“企业数字中枢”

2023年4月18日&#xff0c;北京——今日&#xff0c;微软宣布由世纪互联运营的Microsoft Teams推出一系列创新功能&#xff0c;围绕企业数字核心能力&#xff0c;赋能数字化协作空间、智能化协作体验、整合生产力工具和工作流、安全合规、构建团队文化等五大落地场景&#xff0…

基于重要抽样技术的非序贯蒙特卡洛法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

前端学习:HTML响应式设计、计算机代码、语义元素

目录 HTML响应Web设计 一、什么是响应式Web设计&#xff1f; 1.流体网格 2.媒体查询 3.响应媒体 4.视口元标记 二、使用Bootstrap HTML计算机代码元素 HTML 语义元素 一、什么是语义元素 二、HTML5中的新的语义元素 HTML响应Web设计 一、什么是响应式Web设计&…

现在学习Java,还有出路吗?

当然有出路&#xff0c;Java一直都是市场占有率最高的编程语言&#xff0c;我们生活涉及到的方方面面都有Java的身影&#xff0c;Java基本也覆盖了所有的行业。同时Java自身也是不断在升级更新&#xff0c;平均一年半左右进行一次&#xff0c;而未来的发展还会更加的强势。 随…

Mysql安装步骤

1、解压服务端Mysql安装包 解压之后的目录就是以上这样的。 2.复制改变my.ini文件 把my.ini文件添加到目录中去 [mysql] # 设置mysql客户端默认字符集 default-character-setutf8 [mysqld] #设置3306端口 port 3306 # 设置mysql的安装目录 basedirE:/mysql/mysql-8.0.18-wi…

Spring AOP核心概念与操作示例

AOP 核心概念 还记得我们Spring有两个核心的概念嘛&#xff1f;一个是IOC/DI&#xff0c;另一个是AOP咯。 先来认识两个概念&#xff1a; AOP(Aspect Oriented Programming)面向切面编程&#xff1b;作用&#xff1a;在不惊动原始设计的基础上为其进行功能增强&#xff0c;类…

Linux命令行操作/选项介绍,文件分类/内容与属性/绝对相对路径,隐藏文件与整个目录结构

Linux的命令行操作介绍 Linux操作的特点&#xff1a;纯命令行&#xff0c;当然Linux它也有图形化界面或桌面版。Windows也有命令行&#xff0c;也有图形化界面。不过它是面向普通客户的操作系统&#xff0c;所以必须得是好用好玩的&#xff0c;所以图形化界面那是必然。无论是…