TCP流套接字编程
文章目录
- 前言
- TCP 和 UDP 的特点对比
- TcpEchoServer 服务端实现
- 1. 创建 ServerSocket 类实现通信双方建立连接
- 2. 取出建立的连接实现双方通信
- 3. 服务端业务逻辑实现
- 关闭资源
- 服务端整体代码
- TcpEchoClient 客户端实现
- 1. 创建出 Socket 对象来与服务端实现通信
- 2. 实现客户端的主要逻辑
- 服务端整体代码
- 功能实现
- 多个客户端访问服务器
- 优化后的服务端代码
- 根据回显服务器实现一个简单的字典功能
前言
前面我们学习了使用 UDP 数据报实现套接字编程,因为 UDP 和 TCP 存在一些差异,所以在实现 TCP 流套接字编程的时候也会出现一些不同的做法,这篇文章我将为大家分享如何使用 TCP 流实现套接字编程。
TCP 和 UDP 的特点对比
要想实现 TCP 流的套接字编程就需要先知道 TCP 和 UDP 的区别。
- TCP 是有连接的,UDP 是无连接的。
- TCP 是可靠传输,UDP 是不可靠传输。
- TCP 是面向字节流,UDP 是面向数据报。
- TCP 和 UDP 都是全双工的。
这里实现 TCP 流套接字编程与 UDP 数据报套接字编程的区别则主要体现在 TCP 有连接,UDP 无连接;TCP 面向字节流,UDP 面向数据报。
TcpEchoServer 服务端实现
1. 创建 ServerSocket 类实现通信双方建立连接
要想实现 TCP 通信,首先需要通信双方建立连接。在 Java 中,TCP 双方建立连接依靠 ServerSocket
接口,ServerSocket
底层类似一个阻塞队列,当客户端和服务端建立连接之后,ServerSocket
会通过内核将这些建立好的连接给依次存储下来,当双方需要进行通信的时候服务器就会将这个连接从内核中取出来,然后进行通信。
public class TcpEchoServer {ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {//服务端需要指定端口号serverSocket = new ServerSocket(port);}
}
2. 取出建立的连接实现双方通信
通过 accept()
方法可以取出建立的连接进行双方的通信。
public void start() throws IOException {System.out.println("服务端启动");while (true) {Socket clientSocket = serverSocket.accept();//进行通信processConnection(clientSocket);}
}
3. 服务端业务逻辑实现
TCP 是面向字节流的传输,不像 UDP 依靠数据报传输,所以在这里需要用到前面文件操作的 InputStream
和 OutputStream
来实现请求的读取和响应的发送了。
public void processConnection(Socket clientSocket) {//提示客户端上线System.out.printf("[%s %d] 客户端线\n",clientSocket.getInetAddress(),clientSocket.getPort());try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {} catch (IOException e) {throw new RuntimeException(e);}
}
读取字节数据的时候会显得比较麻烦,所以可以使用 Scanner
来简化读取过程。
scanner.next()
方法读取到空白符(空格、回车、制表符)结束,这正好与我们客户端输入请求的时候以空白符结束对应。
Scanner scanner = new Scanner(inputStream);
while (true) {if (!scanner.hasNext()) {System.out.printf("[%s %d] 客户端下线",clientSocket.getInetAddress(),clientSocket.getPort());break;}String request = scanner.next();
}
process(String request)
方法实现对请求数据的处理。
String response = process(request);public String process(String request) {return request;
}
当服务端接收到 process()
方法返回的数据之后,就需要将处理的数据返回给服务端,因为同样是面向字节流,所以需要依靠 OutputStream 类来将数据传输给服务端,但是字节流操作较麻烦,所以可以使用 PrintWriter
类封装一下 OutputStream
类来简化操作。
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(response);
printWriter.flush();
//打印出服务端日志
System.out.printf("[%s %d] req=%s res=%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);
关闭资源
在 TCP 流实现套接字编程的时候,如果不关闭资源的话就会出现资源泄露的问题,那么为什么会出现资源泄露呢?当服务器和新的客户端实现双方通信的时候就是调用 serverSocket.accept()
方法创建出新的 Socket
资源,而且这个 Socket
资源并不是一直贯穿服务端程序始终的,所以在这里就需要及时关闭资源,防止发生资源泄露的问题。
但是呢,我们不是使用了 try-with-resources
模型吗,当这个模型当中的代码执行完成了之后不是会自动调用里面的对象的 close
方法吗?是的,这里没问题,但是我们在这个模型中创建的是 InputStream
对象和 OutputStream
对象,程序结束的时候关闭的是流对象,而 Socket
对象并没有关闭,所以就还需要我们呢手动关闭这个 Socket
资源。
我们这里使用的是 finally
代码块来关闭资源,防止中间代码出现异常而导致资源没有成功关闭。
finally {clientSocket.close();
}
服务端整体代码
package netWork;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 {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();processConnection(clientSocket);}}public void processConnection(Socket clientSocket) throws IOException {System.out.printf("[%s %d] 客户端上线\n",clientSocket.getInetAddress(),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()) {System.out.printf("[%s %d] 客户端下线",clientSocket.getInetAddress(),clientSocket.getPort());break;}String request = scanner.next();String response = process(request);printWriter.println(response);printWriter.flush();System.out.printf("[%s %d] req=%s res=%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}} catch (IOException e) {throw new RuntimeException(e);} finally {clientSocket.close();}}public String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer = new TcpEchoServer( 9906);tcpEchoServer.start();}
}
TcpEchoClient 客户端实现
1. 创建出 Socket 对象来与服务端实现通信
客户端实现与服务端的通信依赖于 Socket
接口。
public class TcpEchoClient {Socket socket = null;public TcpEchoClient(String serverIp, int serverPort) throws IOException {socket = new Socket(serverIp,serverPort);}
}
虽然 TCP 通信,双方会保存对方的信息,但是我们在客户端代码中还是需要指定出 目的 IP 和 目的端口。
2. 实现客户端的主要逻辑
提示用户输入请求数据。
public void run() {Scanner scanner = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {Scanner netWorkScanner = new Scanner(inputStream);while (true) {String request = scanner.next();}} catch (IOException e) {throw new RuntimeException(e);}}
将请求的数据通过 OutputStream
传输。
PrintWriter printWriter = new PrintWriter(outputStream);
printWriter.println(request);
printWriter.flush();
使用 InputStream
读取服务端传回的数据。
Scanner netWorkScanner = new Scanner(inputStream);
String response = netWorkScanner.next();
System.out.println(response);
服务端整体代码
package netWork;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(String serverIp, int serverPort) throws IOException {// 需要在创建 Socket 的同时, 和服务器 "建立连接", 此时就得告诉 Socket 服务器在哪里~~// 具体建立连接的细节, 不需要咱们代码手动干预. 是内核自动负责的.// 当我们 new 这个对象的时候, 操作系统内核, 就开始进行 三次握手 具体细节, 完成建立连接的过程了.socket = new Socket(serverIp, serverPort);}public void start() {// tcp 的客户端行为和 udp 的客户端差不多.// 都是:// 3. 从服务器读取响应.// 4. 把响应显示到界面上.Scanner scanner = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {PrintWriter writer = new PrintWriter(outputStream);Scanner scannerNetwork = new Scanner(inputStream);while (true) {// 1. 从控制台读取用户输入的内容System.out.print("-> ");String request = scanner.next();// 2. 把字符串作为请求, 发送给服务器// 这里使用 println, 是为了让请求后面带上换行.// 也就是和服务器读取请求, scanner.next 呼应writer.println(request);writer.flush();// 3. 读取服务器返回的响应.String response = scannerNetwork.next();// 4. 在界面上显示内容了.System.out.println(response);}} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) throws IOException {TcpEchoClient client = new TcpEchoClient("192.168.144.1", 9090);client.start();}
}
功能实现
多个客户端访问服务器
既然是网络编程,那肯定离不开多个客户端同时访问一个服务器的情况,如果我们要想使用多个客户端同时访问同一个服务器能行吗?
在 IDEA 中如何实现同一个代码两个进程呢?
当多个客户端同时访问这个服务器的时候,我们可以发现:这个功能并没有真正的实现。那么为什么这个代码不能实现多个客户端同时访问同一个服务器的情况呢?
通过观察服务器端的代码,可以发现:其实这里是有两个 while 循环的,第一个循环是用来取得和多个客户端的连接的,但是为什么这里没有取得和我们的第二个客户端的连接呢?这里第一个客户端在和服务端进行通信的时候,是处在第二个 while 循环当中的,并且这里第一个客户端并没有与服务器断开连接,也就是说,当前服务器还处于第二个 while 循环中等待第一个客户端发送请求,所以当第二个客户端想要和服务器进行通信的时候就不能到达 serverSocket.accept()
方法与服务器进行通信。那么如何解决这个问题呢?
这就需要用到我们前面学习的多线程的知识了,创建多个线程进行 processConnection()
方法,这样就可以使的另一个客户端想要和服务器进行通信的时候就可以在另一个线程中得到和服务器的连接。
public void start() throws IOException {System.out.println("服务端启动");while (true) {Socket clientSocket = serverSocket.accept();Thread t = new Thread(() -> {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});t.start();}
}
但是这样频繁的创建和销毁线程也会消耗较多的资源,所以这里可以做出优化:使用线程池,虽然这里线程池可以优化,但是优化的不多。
public void start() throws IOException {System.out.println("服务端启动");ExecutorService service = Executors.newCachedThreadPool();while (true) {Socket clientSocket = serverSocket.accept();service.submit(new Runnable() {@Overridepublic void run() {try {processConnection(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}}});}}
其实这里处理多个客户端同一时间访问同一个服务器的最好方法就是 IO 多路复用 / IO 多路转接。
IO多路复用是一种同步IO模型,它允许在单个进程/线程内同时处理多个IO请求。具体来说,一个进程/线程可以监视多个文件句柄,一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作。如果没有文件句柄就绪,应用程序将被阻塞,并交出CPU。
这种模型允许多个请求共享同一个进程/线程,使得在处理大量请求时,可以更有效地利用系统资源。如果每个请求都使用独立的进程/线程来处理,那么系统需要创建和管理大量的进程/线程,这将消耗大量的系统资源。而使用IO多路复用技术,可以复用一个或几个线程来处理多个TCP连接,从而大大减少了系统开销。
IO多路复用技术的出现主要是为了解决阻塞IO的问题。在操作系统中,最初只有BIO模式,即阻塞IO。当一个请求被处理时,如果该请求需要等待IO操作完成(例如读写操作),则该进程/线程将被阻塞,直到IO操作完成。这会导致系统资源的浪费,尤其是在需要处理大量请求的情况下。
IO多路复用技术的引入解决了这个问题。通过监视多个文件句柄,一个进程/线程可以在同一时间处理多个IO请求,从而提高了系统的效率。当一个文件句柄就绪时,该进程/线程可以立即进行相应的读写操作,而不需要等待其他请求完成。这种方式可以有效地减少系统资源的浪费,并提高系统的吞吐量。
IO多路复用技术被广泛应用在网络编程中,特别是服务器端编程。由于服务器需要同时处理来自大量客户端的请求,因此使用IO多路复用技术可以提高服务器的性能和响应速度。例如,Nginx服务器就使用了IO多路复用技术来处理大量的客户端连接。
这里具体怎么解决我这里及不过多叙述了,大家有兴趣可以去学习学习。
优化后的服务端代码
package netWork1;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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;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) {// 通过 accept 方法, 把内核中已经建立好的连接拿到应用程序中.// 建立连接的细节流程都是内核自动完成的. 应用程序只需要 "捡现成" 的.Socket clientSocket = serverSocket.accept();// 此处不应该直接调用 processConnection, 会导致服务器不能处理多个客户端.// 创建新的线程来调用更合理的做法.// 这种做法可行, 不够好
// Thread t = new Thread(() -> {
// processConnection(clientSocket);
// });
// t.start();// 更好一点的办法, 是使用线程池.service.submit(new Runnable() {@Overridepublic void run() {processConnection(clientSocket);}});}}// 通过这个方法, 来处理当前的连接.public void processConnection(Socket clientSocket) {// 进入方法, 先打印一个日志, 表示当前有客户端连上了.System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());// 接下来进行数据的交互.try (InputStream inputStream = clientSocket.getInputStream();OutputStream outputStream = clientSocket.getOutputStream()) {// 使用 try ( ) 方式, 避免后续用完了流对象, 忘记关闭.// 由于客户端发来的数据, 可能是 "多条数据", 针对多条数据, 就循环的处理.while (true) {Scanner scanner = new Scanner(inputStream);if (!scanner.hasNext()) {// 连接断开了. 此时循环就应该结束System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());break;}// 1. 读取请求并解析. 此处就以 next 来作为读取请求的方式. next 的规则是, 读到 "空白符" 就返回.String request = scanner.next();// 2. 根据请求, 计算响应.String response = process(request);// 3. 把响应写回到客户端.// 可以把 String 转成字节数组, 写入到 OutputStream// 也可以使用 PrintWriter 把 OutputStream 包裹一下, 来写入字符串.PrintWriter printWriter = new PrintWriter(outputStream);// 此处的 println 不是打印到控制台了, 而是写入到 outputStream 对应的流对象中, 也就是写入到 clientSocket 里面.// 自然这个数据也就通过网络发送出去了. (发给当前这个连接的另外一端)// 此处使用 println 带有 \n 也是为了后续 客户端这边 可以使用 scanner.next 来读取数据.printWriter.println(response);// 此处还要记得有个操作, 刷新缓冲区. 如果没有刷新操作, 可能数据仍然是在内存中, 没有被写入网卡.printWriter.flush();// 4. 打印一下这次请求交互过程的内容System.out.printf("[%s:%d] req=%s, resp=%s\n", clientSocket.getInetAddress(), clientSocket.getPort(),request, response);}} catch (IOException e) {e.printStackTrace();} finally {try {// 在这个地方, 进行 clientSocket 的关闭.// processConnection 就是在处理一个连接. 这个方法执行完毕, 这个连接也就处理完了.clientSocket.close();} catch (IOException e) {e.printStackTrace();}}}public String process(String request) {// 此处也是写的回显服务器. 响应和请求是一样的.return request;}public static void main(String[] args) throws IOException {TcpEchoServer server = new TcpEchoServer(9906);server.start();}
}
根据回显服务器实现一个简单的字典功能
这个功能服务器的业务逻辑相比于简单的回显就稍复杂了一点,但是也没有复杂很多,只是需要更改一下服务端的 process
方法,在这个方法中使用 Map
存储几个单词的翻译就好了。
package netWork;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;public class TcpDictServer extends TcpEchoServer {Map<String, String> dict = new HashMap<>();public TcpDictServer(int port) throws IOException {super(port);dict.put("cat","小猫");dict.put("dog","小狗");dict.put("bird","小鸟");dict.put("pig","小猪");}@Overridepublic String process(String request) {return dict.getOrDefault(request,"您查找的单词在该词典中不存在");}public static void main(String[] args) throws IOException {TcpDictServer tcpDictServer = new TcpDictServer(9906);tcpDictServer.start();}
}
相关文章:

TCP流套接字编程
文章目录 前言TCP 和 UDP 的特点对比TcpEchoServer 服务端实现1. 创建 ServerSocket 类实现通信双方建立连接2. 取出建立的连接实现双方通信3. 服务端业务逻辑实现关闭资源服务端整体代码 TcpEchoClient 客户端实现1. 创建出 Socket 对象来与服务端实现通信2. 实现客户端的主要…...
Python迭代器创建与使用:从入门到精通
一、可迭代对象 1、 什么是可迭代对象? 表示可以逐一迭代或者遍历的对象,序列:列表、元组、集合、字符串。非序列:字典、文件。自定义对象:实现了__iter__()方法的对象;实现了使用整数索引的 getitem()方…...

mac虚拟机安装homebrew时的问题
安装了mac虚拟机,结果在需要通过“brew install svn”安装svn时,才注意到没有下载安装homebrew。 于是便想着先安装homebrew,网上查的教程大多是通过类似以下命令 “ruby <(curl -fsSkL raw.github.com/mxcl/homebrew/go)” 但是都会出现…...

学信息系统项目管理师第4版系列32_信息技术发展
1. 大型信息系统 1.1. 大型信息系统是指以信息技术和通信技术为支撑,规模庞大,分布广阔,采用多级 网络结构,跨越多个安全域;处理海量的,复杂且形式多样的数据,提供多种类型应用 的大系统 1.1.…...

Vue3 + Nodejs 实战 ,文件上传项目--大文件分片上传+断点续传
目录 1.大文件上传的场景 2.前端实现 2.1 对文件进行分片 2.2 生成hash值(唯一标识) 2.3 发送上传文件请求 3.后端实现 3.1 接收分片数据临时存储 3.2 合并分片 4.完成段点续传 4.1修改后端 4.2 修改前端 5.测试 博客主页:専心_前端…...

宏(预编译)详解
目录 一、程序的编译环境 二、运行环境 三、预编译详解 3.1预定义符号 3.2.1 #define 定义标识符 3.2.2 #define 定义宏 3.2.3#define替换规则 3.2.4 #和## 2)##的作用: 3.2.5宏和函数的对比 3.2.6宏的命名约定和#undef指令 一、命名约定: …...

hue实现对hiveserver2 的负载均衡
如果你使用的是CDH集群那就很是方便的 在Cloudera Manager中,进入HDFS Service 进入Instances标签页面,点击Add Role Instances按钮,如下图所示 点击Continue按钮,如下图所示 返回Instances页面,选择HttpFS角色…...
SkyWalking 告警规则配置说明
Skywalking告警功能是在6.x版本新增的,其核心由一组规则驱动,这些规则定义在config/alarm-settings.yml 文件中。告警规则定义分为两部分: 1、告警规则:它们定义了应该如何触发度量警报,应该考虑什么条件 2、webhook(网络钩子):定义当告警触发时,哪些服务终端需要被…...

HTML 表单笔记/练习
表单 概述 表单用于收集用户信息,用户填写表单提交到服务器 一般传参方式: GETPOSTCookie 传参要素 传参方式 GETPOST 参数的名字目标页面内容的数据类型(只有在上传文件的时候) 提示信息 一个表单中通常还包含一些说明性的文…...
关于Java Integer和Long使用equals直接比较
Integer和Long不能直接equals比较会返回False Long.class源码 public boolean equals(Object obj) {if (obj instanceof Long) {return this.value (Long)obj;} else {return false;} }Integer.class源码 public boolean equals(Object obj) {if (obj instanceof Integer) {…...

nodejs+vue衣服穿搭推荐系统-计算机毕业设计
模块包括主界面,系统首页、个人中心、用户管理、风格标签管理、衣服分类管理、衣服穿搭管理、服装信息管理、我的搭配管理、用户反馈、系统管理等进行相应的操作。无论是日常生活,还是特定场景,诸如面试、约会等,人们都有展现自我…...

Java并发面试题:(七)ThreadLocal原理和内存泄漏
ThreadLocal是什么? ThreadLocal是线程本地存储机制,可以将数据缓存在线程内部。ThreadLocal存储的变量在线程内共享的,在线程间又是隔离的。 ThreadLocal实现原理? ThreadLocal的底层是ThreadLocalMap,每个Thread都…...

香港服务器在国内访问太慢怎么能提高?
一直以来,全球化业务需求的增长是跟随着蓬勃向上的互联网而发展的。有了网络,海外贸易就在鼠标的轻点中完成。而IDC市场中的香港服务器也因为免备案政策的特性,开始逐渐成为企业想要跨越地域壁垒而考虑的对象。但在使用过程中ÿ…...

使用Proxyman抓取Android的https请求
使用Proxyman抓取Android的https请求 有时,您可能需要测试您的移动应用程序并检查与其关联的所有网络请求。在网络上,此任务非常简单,只需按Ctrl Shift I打开开发人员工具即可。从那里,您可以导航到网络选项卡并检查与网页相关的…...

基础MySQL的语法练习
基础MySQL的语法练习 create table DEPT(DEPTNO int(2) not null,DNAME VARCHAR(14),LOC VARCHAR(13) );alter table DEPTadd constraint PK_DEPT primary key (DEPTNO);create table EMP (EMPNO int(4) primary key,ENAME VARCHAR(10),JOB VARCHAR(9),MGR …...
RAID和LVM配置指南:创建、扩容和管理RAID设备和逻辑卷的方法
文章目录 1. 简介1.1 什么是RAID和LVM1.2 RAID和LVM的作用和优势 2. RAID配置命令:mdadm2.1 安装mdadm2.2 创建RAID设备2.2.1 RAID 02.2.2 RAID 12.2.3 RAID 52.2.4 RAID 10 2.3 添加磁盘到RAID设备2.4 删除磁盘从RAID设备2.5 查看和管理RAID设备2.6 故障处理与恢复…...
MapStruct使用方法
一、用途 1.1 优势 与动态映射框架相比,MapStruct 具有以下优势: (1)通过使用普通方法getter、setter调用,而不是反射来快速执行,效率很高。 (2)编译时类型安全:只能映…...
【LeetCode】50. Pow(x, n)
1 问题 实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即, x n x^n xn )。 示例 1: 输入:x 2.00000, n 10 输出:1024.00000 示例 2: 输入:x 2.10000, n 3 输出&a…...
vue2技能树(2)-模板语法、vue的工具链、渐进式框架
目录 Vue2技能树Vue 2 简单的模板语法详解插值绑定属性指令v-if 和 v-elsev-forv-on 计算属性过滤器插槽 Vue 2 生态系统详解1. Vue Router2. Vuex3. Vue CLI4. Axios5. Vue Devtools6. Element UI、Vuetify、Quasar等UI框架7. Nuxt.js8. Vue Apollo、Vue Router、Vue Fire等插…...
【Git系列教程-目录大纲】
《Git系列教程-目录大纲》 完完全全从零开始深入学习Git,教程配图200张,其中包括包括Git基本命令、命令原理、Git底层命令、分支、分支的原理、Git代码冲突原理/解决、tag标签、Git存储状态、分支合并原理、典型合并、快进合并、同轴开发、非同轴开发、…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...

解析两阶段提交与三阶段提交的核心差异及MySQL实现方案
引言 在分布式系统的事务处理中,如何保障跨节点数据操作的一致性始终是核心挑战。经典的两阶段提交协议(2PC)通过准备阶段与提交阶段的协调机制,以同步决策模式确保事务原子性。其改进版本三阶段提交协议(3PC…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...