当前位置: 首页 > article >正文

JAVA网络编程——socket套接字的介绍下(详细)

目录

前言

1.TCP 套接字编程 与 UDP 数据报套接字的区别

2.TCP流套接字编程 

API 介绍

TCP回显式服务器

Scanner 的多种使用方式

PrintWriter 的多种使用方式

 TCP客户端

3. TCP 服务器中引入多线程

结尾

前言

各位读者大家好,今天笔者继续更新socket套接字的下半部分,这部分承接上半篇的内容接着往下写

JAVA网络编程——socket套接字的介绍上(详细)-CSDN博客

上半篇博客笔者主要汇总了一些网络编程的基础知识,并介绍了UDP数据报套接字编程和代码案例

实现了一个简单的回显服务器和客户端

博客的主要内容如下:

1.TCP流套接字编程 与 UDP 数据报套接字的区别,并结合具体代码,说明两者在实现方式上的差异。

2.TCP流套接字 编程常用 API 和代码示例,并尝试引入多线程思路对服务端代码进行优化。

3.最后,笔者还将分享一些学习过程中遇到的问题和经验,希望能对读者有所启发。

本篇博客的内容会和上半篇博客有联系,但您依旧可以把他看作是独立的知识博客,无需太多阅读门槛,言尽于此,就让我们开始吧!

1.TCP 套接字编程 与 UDP 数据报套接字的区别

在实际编程之前,我们首先要了解:为什么 TCP 和 UDP 的套接字写法会有所不同?

 从传输层协议的角度来看, TCP和UDP是不同的协议

TCP 是一种面向连接的传输层协议,在通信前需要通过“三次握手”建立连接,通信过程中也会通过校验、序号、确认应答等机制保证数据的可靠传输。可以理解为,TCP 的通信就像是打电话,必须先拨号接通,双方确认后才能说话。

 而 UDP 是一种无连接的协议,发送数据前并不建立连接,也不会确认数据是否成功送达。UDP 更像是发快递,你把包裹投递出去就完事了,送达与否不做额外处理。这种机制导致 UDP 的通信过程更加简单高效,但也更容易出现数据丢失、乱序的问题。

也正因为如此,在编程中,两者的使用方式也大相径庭。

对于UDP套接字编程,正如前文提到的服务端通过 DatagramSocket 创建一个端口并监听,客户端发送数据时需要用 DatagramPacket 显式地指定目标 IP 和端口号。

  通俗来说 UDP就像发短信

  • 直接发送,不管对方是否收到
  • 发完就完了,不知道对方看没看到
  • 快速简单,但不保证送达

 相比之下,TCP 流套接字编程更加结构化。服务端使用 ServerSocket 来监听端口,当有客户端发起连接时,会通过 accept() 方法返回一个与客户端进行一对一通信的 Socket 对象。在这之后,服务端和客户端之间就建立起了一个稳定的通信通道,后续的通信就像操作输入输出流一样,不需要再关注 IP 和端口这些底层细节。

换句话说:TCP读取数据的基本单位是字节,TCP 协议本身是面向字节流(byte stream)的协议

 同样通俗的说 , TCP就像打电话

  • 拨号建立连接,确认对方接听后才开始说话
  • 说话过程中可以确认对方是否听到
  • 如果线路有问题会重新说一遍
  • 说完话要挂断电话结束通话

也就是说,UDP 更加“灵活”和“轻便”,每次通信都要说明来龙去脉(目标地址);而 TCP 则是“关系型”的,一旦连接建立,双方就可以安心地进行数据交换,程序的逻辑也更加清晰。

接下来笔者开始介绍API和代码示例,并介绍TCP套接字的代码是如何体现它和UDP的区别的. 

2.TCP流套接字编程 

 和UDP数据报套接字编程类似,TCP流套接字编程 同样有两个重要的API

API 介绍

1.ServerSocket
ServerSocket 这个类用于服务器端监听特定端口,等待客户端来连接。 它体现了 TCP 协议中"先建立连接,再通信”的特性。

 ServerSocket 构造方法

ServerSocket serverSocket = new ServerSocket(int port);

其中 int port 表示 监听的端口号 ,一旦绑定成功,port端口就会被占用,等待客户端连接

常用方法:

1.Socket accept()

    开始监听指定端⼝(创建时绑定的端⼝),有客⼾端连接后,返回⼀个服务端Socket对象,并基于该Socket建⽴与客⼾端的连接,否则阻塞等待
具体一点说:

accept() 方法执行以后:

服务器端和客户端之间就建立起了一条可靠的 TCP 连接,双方都会在系统内核中保存关于对方的关键信息,如:

  • 客户端的 IP 地址

  • 客户端的端口号

  • 服务器的 IP 地址

  • 服务器的端口号

这 4 个信息,加上传输协议(TCP),就构成了所谓的 “五元组”,唯一标识一条 TCP 连接。这也体现了 TCP协议和UDP协议之间的区别

2.Socket close()

    关闭此套接字

2. Socket

Socket 是客户端/服务端之间的通信通道

客户端和服务端之间每一次连接,都会被封装成一个 Socket 对象,负责双方的输入输出数据流。

Socket 构造方法

 构造方法(客户端使用):

Socket socket = new Socket(String host, int port);

其中 host 表示 IP 地址,port表示端口

常用方法:

1.InputStream getInputStream()


获取输入流,用于读取从对端发送过来的字节数据。

2.OutputStream getOutputStream()


获取输出流,用于向对端发送字节数据。

3.close()


关闭当前连接。

主要的API介绍完了,接下来我们通过代码示例来展示他们如何使用,笔者还是先展示一个回显服务器+客户端

TCP回显式服务器

根据上面对 ServerSocket 的介绍,我们可以知道:

在编写一个 TCP 回显式服务器时,首先需要创建一个 ServerSocket 实例并指定监听的端口号。这个操作会在当前机器上开启一个服务器进程,等待客户端的连接请求

一旦有客户端连接到这个端口,服务器就会通过 accept() 方法接收这个连接请求,并返回一个新的 Socket 对象。这个 Socket 对象就代表了服务器与某个客户端之间的一条通信通道

public class TcpEchoServer {ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);// 绑定端口号
//      和 UDP 不同,TCP 在通信前必须先建立连接(就是三次握手)。
// 所以服务器要做的第一件事:不是接收数据,而是 等待客户端来连我。}
}

然后我们启动服务器:

    public void start() throws IOException {System.out.println("服务器启动了");Socket socket = serverSocket.accept(); // 建立连接processwork(socket);}

 其中 processwork () 方法内来实现服务器的功能

而我们的服务器主要实现的功能为:

1.读取请求

2.响应

3.返回请求

请看代码:

    private  void processwork(Socket socket) throws IOException {// 1.读取请求// 2.响应// 3.返回响应try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){// 包装一下Scanner scanner = new Scanner(inputStream);//填入什么参数,就代表了什么输入方式PrintWriter printWriter = new PrintWriter(outputStream);while(true) {String request = scanner.nextLine();// 读取请求String response = process(request); // 回显printWriter.println(response); // 返回响应printWriter.flush(); // 刷新缓冲区,确保已经返回//            4.打印日志System.out.printf("[%s:%d] request: %s, response: %s\n",socket.getInetAddress().toString(),socket.getPort(),request,response);}}finally {socket.close();}}

   InputStream 表示从客户端读入数据的字节流,OutputStream 表示写数据给客户端的字节流

使用了 try-with-resources 语法,能确保在代码执行完毕后,自动关闭资源(这里是输入输出流)。这种写法简洁、安全,避免了流忘记关闭导致的资源泄露问题。

 使用 Scanner 包装输入流,能够将字节数据按照「行文本」的形式解析,方便我们通过 scanner.nextLine() 读取一整行字符串。相比直接用 inputStream.read() 读取字节数组,这种方式更直观、适合处理文本请求。

我们都知道,我们常用 Scanner scanner = new Scanner(System.in); 这代表控制台输入,这是我们初学时学会的,但实际上 Scanner 会根据你传入参数的不同表示不同的输入方法

Scanner 的多种使用方式

1. 控制台输入:

Scanner scanner = new Scanner(System.in);

从键盘读取用户输入,适合交互式程序。


2. 从文件中读取:

Scanner scanner = new Scanner(new File("input.txt"));

读取文件内容,适用于处理数据文件或配置文件等场景。


3. 从字符串中读取:

Scanner scanner = new Scanner("Hello World\n123");

可以将字符串当作输入源,非常适合字符串解析。


4. 从网络输入流中读取(Socket 网络通信中):

InputStream inputStream = socket.getInputStream();Scanner scanner = new Scanner(inputStream);

这是网络编程中常见的用法,此时 Scanner 会从网络连接中读取对方(比如客户端)发送来的数据。比如 TCP 编程中,服务器端使用 socket.getInputStream() 获取输入流,从客户端接收数据。

同理

PrintWriter 是对输出流的包装,能够方便地以「文本行」的形式输出数据,比如我们可以用 printWriter.println(response) 将一行响应发出去。注意它默认有缓冲机制,发送数据前必须手动调用 flush() 刷新缓冲区,确保数据立即写出。 

同理,我在这里汇总一下

PrintWriter 的多种使用方式

1. 输出到控制台

PrintWriter writer = new PrintWriter(System.out);
writer.println("Hello, Console!");
writer.flush();

2. 输出到文件

PrintWriter writer = new PrintWriter(new File("output.txt")); 
writer.println("Write this to file."); writer.flush(); writer.close();
  • 输出目标:磁盘文件

  • 适用场景:日志记录、写入结果、持久化数据

3. 输出到网络(如 TCP 编程)

OutputStream outputStream = socket.getOutputStream(); 
PrintWriter writer = new PrintWriter(outputStream); 
writer.println("Hello, client!");
writer.flush();
  • 输出目标:通过 TCP 连接的对方客户端(或服务器)

  • 适用场景:TCP 套接字通信中,服务器/客户端发送响应或数据

4. 输出到内存字符串缓冲区

StringWriter stringWriter = new StringWriter();PrintWriter writer = new PrintWriter(stringWriter);writer.println("Write to memory."); 
writer.flush(); 
String result = stringWriter.toString(); // 获取最终字符串
  • 输出目标:内存中的字符串

  • 适用场景:构建复杂字符串、模板处理、动态拼接内容

最后,无论通信过程中是否抛出异常,最终都要关闭 socket。这是网络编程中很重要的一步,必须手动释放网络资源,避免连接堆积导致服务器崩溃。

那么为什么之前的UDP不用?

本质原因:UDP 是无连接的

TCP 是一种面向连接的协议,通信前需要建立连接,通信后要关闭连接,连接的生命周期是明确的,因此必须显式关闭以释放资源。

而 UDP 是无连接的协议DatagramSocket 并不会维持一个长时间的连接状态。它只是一个发送/接收数据报的工具,就像一个“邮筒”或“信箱”,你发完信或者收完信就可以不管它了,系统会随着进程结束自动回收资源。

 因此完整的服务器代码如下:

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 {ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);// 绑定端口号
//      和 UDP 不同,TCP 在通信前必须先建立连接(就是三次握手)。
// 所以服务器要做的第一件事:不是接收数据,而是 等待客户端来连我。}public void start() throws IOException {System.out.println("服务器启动了");Socket socket = serverSocket.accept(); // 建立连接processwork(socket);}private  void processwork(Socket socket) throws IOException {// 1.读取请求// 2.响应// 3.返回响应try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){// 包装一下Scanner scanner = new Scanner(inputStream);//填入什么参数,就代表了什么输入方式PrintWriter printWriter = new PrintWriter(outputStream);while(true) {String request = scanner.nextLine();// 读取请求String response = process(request); // 回显printWriter.println(response); // 返回响应printWriter.flush(); // 刷新缓冲区,确保已经返回//            4.打印日志System.out.printf("[%s:%d] request: %s, response: %s\n",socket.getInetAddress().toString(),socket.getPort(),request,response);}}finally {socket.close();}}private  String process(String request){return request;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);tcpEchoServer.start();}
}

 TCP客户端

 接下来是客户端,他大概有两个功能

1.构造请求,并发送给服务器

2.读取服务器发送回来的请求

我们首先创建一个 Socket 对象,并且绑定好IP地址和端口号

public class TcpEchoClient{private Socket socket = null;public TcpEchoClient(String serverIp, int serverPort) throws IOException {// 连接服务器socket = new Socket(serverIp, serverPort);}
}

 然后实现功能:

代码和服务器原理一致,笔者就不过多细说了

    public void start() throws IOException{System.out.println("客户端启动,已连接服务器");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){// 1.读取内容System.out.println("输入内容");String request = scanner.nextLine();//  2.发送给服务器printWriter.println(request);// 加上ln, 暗中约定一个请求以\n作为结尾// 刷新缓冲区,保证数据能正确发出去printWriter.flush();// 3.读取服务器返回的响应String response = scannerNet.nextLine();//4.打印返回的数据System.out.println(response);}}catch (IOException e){throw  new RuntimeException(e);}}

 最后效果如下:

3. TCP 服务器中引入多线程

接下来我们谈一下 多线程的问题

在最初的 TCP 服务器代码中,我们使用如下方式来接收客户端连接:

Socket socket = serverSocket.accept();
processwork(socket);

这种写法是单线程、串行处理,意味着服务器一次只能处理一个客户端的请求。当一个客户端连接上来,服务器就会一直处理这个客户端的请求,直到处理完毕后,才能继续 accept() 等待下一个客户端。这带来两个致命问题:

  1. 其他客户端无法同时连接
    如果第一个客户端一直占用服务器,那么第二个客户端连接时,服务器就无法 accept(),连接会阻塞甚至超时。

  2. 服务器响应效率极低
    在网络通信中,尤其是长连接的场景下,一个客户端可能长时间保持连接,服务器如果不使用并发机制,就会严重浪费资源和降低效率。

TCP 的连接机制导致必须并发处理

TCP 是面向连接的协议,通信前需要完成三次握手,通信过程中保持连接状态。每个 Socket 实例都代表一次独立的连接。一旦 accept() 接收了连接,这个 Socket 的处理过程就独占了当前线程。如果不多线程处理,服务器根本无法同时应对多个 Socket 连接。

因此我们可以引入多线程的写法, 那么,我们怎么引入呢?

 笔者选的的是线程池,这样方便管理

来看具体代码:

ExecutorService executorService = Executors.newCachedThreadPool(); // 创建线程池while (true) {Socket socket = serverSocket.accept(); // 接受连接executorService.submit(() -> {         // 把任务提交给线程池try {processwork(socket);           // 多线程处理该连接} catch (IOException e) {throw new RuntimeException(e);}});
}
  • newCachedThreadPool()这是一个可自动扩容的线程池,适合连接数不固定、通信时长不一致的场景;

  • submit(() -> {...})使用 Lambda 表达式将每个客户端的任务包装成线程任务提交;

  • 每个客户端的 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;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class TcpEchoServer {ServerSocket serverSocket = null;public TcpEchoServer(int port) throws IOException {serverSocket = new ServerSocket(port);// 绑定端口号
//      和 UDP 不同,TCP 在通信前必须先建立连接(就是三次握手)。
// 所以服务器要做的第一件事:不是接收数据,而是 等待客户端来连我。}public void start() throws IOException {System.out.println("服务器启动了");ExecutorService executorService = Executors.newCachedThreadPool();//动态增长的线程池while (true) {Socket socket = serverSocket.accept(); // 建立连接executorService.submit(() ->{try {processwork(socket);} catch (IOException e) {throw new RuntimeException(e);}});}
//        Socket socket = serverSocket.accept(); // 建立连接
//        processwork(socket);}private  void processwork(Socket socket) throws IOException {// 1.读取请求// 2.响应// 3.返回响应try(InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()){// 包装一下Scanner scanner = new Scanner(inputStream);//填入什么参数,就代表了什么输入方式PrintWriter printWriter = new PrintWriter(outputStream);while(true) {String request = scanner.nextLine();// 读取请求String response = process(request); // 回显printWriter.println(response); // 返回响应printWriter.flush(); // 刷新缓冲区,确保已经返回//            4.打印日志System.out.printf("[%s:%d] request: %s, response: %s\n",socket.getInetAddress().toString(),socket.getPort(),request,response);}}finally {socket.close();}}private  String process(String request){return request;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);tcpEchoServer.start();}
}

效果如下:

由此可见,可以同步的和服务器进行网络通信

如果多线程依旧无法满足要求,那么还可以考虑多路复用,这个的话以后有时间笔者完全理解了,就写博客分享一下

结尾

这一篇的文本量也达到了1w字往上,希望对于读者们有帮助

后面笔者还想开博客介绍一下 IO操作,HTTPS和HTTP协议,希望能多多支持

相关文章:

JAVA网络编程——socket套接字的介绍下(详细)

目录 前言 1.TCP 套接字编程 与 UDP 数据报套接字的区别 2.TCP流套接字编程 API 介绍 TCP回显式服务器 Scanner 的多种使用方式 PrintWriter 的多种使用方式 TCP客户端 3. TCP 服务器中引入多线程 结尾 前言 各位读者大家好,今天笔者继续更新socket套接字的下半部分…...

Apache SeaTunnel 引擎深度解析:原理、技术与高效实践

Apache SeaTunnel 作为新一代高性能分布式数据集成平台,其核心引擎设计融合了现代大数据处理架构的精髓。 Apache SeaTunnel引擎通过分布式架构革新、精细化资源控制及企业级可靠性设计,显著提升了数据集成管道的执行效率与运维体验。其模块化设计允许用…...

深入理解 Maven 循环依赖问题及其解决方案

在 Java 开发领域,Maven 作为主流构建工具极大简化了依赖管理和项目构建。然而**循环依赖(circular dependency)**问题仍是常见挑战,轻则导致构建失败,重则引发类加载异常和系统架构混乱。 本文将从根源分析循环依赖的…...

pytest中的元类思想与实战应用

在Python编程世界里,元类是一种强大而高级的特性,它能在类定义阶段深度定制类的创建与行为。而pytest作为热门的测试框架,虽然没有直接使用元类,但在设计机制上,却暗含了许多与元类思想相通的地方。接下来,…...

前端生成UUID

UUID(Universally Unique Identifier)是一种在分布式系统中广泛使用的标识符,具有全球唯一性。在前端开发中,生成可靠的UUID对于数据追踪、会话管理、缓存键生成等场景至关重要。接下来将深入探讨UUID的实现原理、前端生成方案及最佳实践。 一、UUID标准与版本 1. UUID结构…...

玩客云WS1608控制LED灯的颜色

玩客云WS1608控制LED灯的颜色 玩客云设备有个红、绿、蓝三色led灯,在刷入armbian系统以后,这个灯的颜色就会显示异常,往往是一直显示红色。 如果要自动动手调整led灯的颜色,控制命令如下(需要root用户执行&#xff0…...

实验三 企业网络搭建及应用

实验三 企业网络搭建及应用 一、实验目的 1.掌握企业网络组建方法。 2.掌握企业网中常用网络技术配置方法。 二、实验描述 某企业设有销售部、市场部、技术部和财务部四个部门。公司内部网络使用二层交换机作为用户的接入设备。为了使网络更加稳定可靠,公司决定…...

顶会新热门:机器学习可解释性

🧀机器学习模型的可解释性一直是研究的热点和挑战之一,同样也是近两年各大顶会的投稿热门。 🧀这是因为模型的决策过程不仅需要高准确性,还需要能被我们理解,不然我们很难将它迁移到其它的问题中,也很难进…...

ReactJS 中的 JSX工作原理

文章目录 前言✅ 1. JSX 是什么?🔧 2. 编译后的样子(核心机制)🧱 3. React.createElement 做了什么?🧠 4. JSX 与组件的关系🔄 5. JSX 到真实 DOM 的过程📘 6. JSX 与 Fr…...

《STL--stack 和 queue 的使用及其底层实现》

引言: 上次我们学习了容器list的使用及其底层实现,相对来说是比较复杂的,今天我们要学习的适配器stack和queue与list相比就简单很多了,下面我们就开始今天的学习: 一:stack(后进先出&#xff…...

ArcGIS Pro 3.4 二次开发 - 地理处理

环境:ArcGIS Pro SDK 3.4 + .NET 8 文章目录 地理处理1 通用1.1 如何执行模型工具1.2 设置地理处理范围环境1.3 在 Geoprocessing 窗格中打开脚本工具对话框1.4 打开特定工具的地理处理工具窗格1.5 获取地理处理项目项1.6 阻止通过GP创建的特征类自动添加到地图中1.7 GPExecut…...

基于springboot的医护人员排班系统设计与实现(源码+文档+部署讲解)

技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…...

Asp.Net Core FluentValidation校验框架

文章目录 前言一、使用步骤1.安装 NuGet 包2.创建模型3.创建验证器4.配置 Program.cs5.创建控制器6.测试结果 二、常见问题及注意事项三、性能优化建议总结 前言 FluentValidation 是一个流行的 .NET 库,用于构建强类型的验证规则。它通常用于验证领域模型、DTO等对…...

CRISPR-Cas系统的小型化研究进展-文献精读137

Progress in the miniaturization of CRISPR-Cas systems CRISPR-Cas系统的小型化研究进展 摘要 CRISPR-Cas基因编辑技术由于其简便性和高效性,已被广泛应用于生物学、医学、农学等领域的基础与应用研究。目前广泛使用的Cas核酸酶均具有较大的分子量(通…...

利用python工具you-get下载网页的视频文件

有时候我们可能在一个网站看到一个视频(比如B站),想下载,但是页面没有下载视频的按钮。这时候,我们可以借助python工具you-get来实现下载功能。下面简要说下步骤 (一)因为使用的是python工具&a…...

Wi-Fi 切换 5G 的时机

每天都希望 Wi-Fi 在我离开信号覆盖范围时能尽快切到 5G,但每次它都能坚挺到最后半格信号,我却连看个天气预报都看不了…我不得不手工关闭 Wi-Fi,然后等走远了之后再打开,如此反复,不厌其烦。 早上出门上班&#xff0c…...

【请关注】各类数据库优化,抓大重点整改,快速优化空间mysql,Oracle,Neo4j等

各类数据库优化,抓大重点整改,快速优化,首先分析各数据库查询全部表的空间大小及记录条数的语句: MySQL -- 查看所有表的空间大小 SELECT TABLE_SCHEMA AS 数据库名, TABLE_NAME AS 表名, ENGINE AS 存储引擎, CONCAT(ROUND(DAT…...

Mybatis Plus JSqlParser解析sql语句及JSqlParser安装步骤

MyBatis Plus 整合 JSqlParser 进行 SQL 解析的实现方案,主要包括环境配置和具体应用。通过 Maven 添加mybatis-plus-core 和 jsqlparser 依赖后,可用 CCJSqlParserUtil 解析 SQL 语句,支持对 SELECT、UPDATE 等语句的语法树分析和重构。技术…...

React从基础入门到高级实战:React 高级主题 - 性能优化:深入探索与实践指南

React 性能优化:深入探索与实践指南 引言 在现代Web开发中,尤其是2025年的技术环境下,React应用的性能优化已成为开发者不可忽视的核心课题。随着用户对应用速度和体验的要求日益提高,React应用的规模和复杂性不断增加&#xff…...

负载均衡群集---Haproxy

目录 一、HAproxy 一、概念 二、核心作用 三、主要功能特性 四、应用场景 五、优势与特点 二、 案例分析 1. 案例概述 2. 案例前置知识点 (1)HTTP 请求 (2)负载均衡常用调度算法 (3)常见的 web …...

2025年5月个人工作生活总结

本文为 2025年5月工作生活总结。 研发编码 一个项目的临时记录 月初和另一项目同事向业主汇报方案,两个项目都不满意,后来领导做了调整,将项目合并,拆分了好几大块。原来我做的一些工作,如数据库、中间件等&#xff…...

【stm32开发板】单片机最小系统原理图设计

一、批量添加网络标签 可以选择浮动工具中的N,单独为引脚添加网络标签。 当芯片引脚非常多的时候,选中芯片,右键选择扇出网络标签/非连接标识 按住ctrl键即可选中多个引脚 点击将引脚名称填入网络名 就完成了引脚标签的批量添加 二、电源引…...

实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.2 R语言解题

本文是实验设计与分析&#xff08;第6版&#xff0c;Montgomery著&#xff0c;傅珏生译) 第5章析因设计引导5.7节思考题5.2 R语言解题。主要涉及方差分析&#xff0c;正态假设检验&#xff0c;残差分析&#xff0c;交互作用。 dataframe<-data.frame( Surfacec(74,64,60,92…...

2025山东CCPC题解

文章目录 L - StellaD - Distributed SystemI - Square PuzzleE - Greatest Common DivisorG - Assembly Line L - Stella 题目来源&#xff1a;L - Stella 解题思路 签到题&#xff0c;因为给出的字母不是按顺序&#xff0c;可以存起来赋其值&#xff0c;然后在比较。 代码…...

【解决办法】ubuntu重启不起来,输入用户名和密码进不去,又重新返回登录页。

项目场景&#xff1a; ubuntu重启不起来&#xff0c;输入用户名和密码进不去&#xff0c;又重新返回登录页。 问题描述 在华硕天选一代笔记本上面安装了ubuntu22.04.5桌面版&#xff0c;但是重启以后出现&#xff0c;输入了用户名和密码&#xff0c;等待一会还让输入用户名和…...

CentOS Stream 9 中部署 MySQL 8.0 MGR(MySQL Group Replication)一主两从高可用集群

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《MySQL技术精粹》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、前言 1、MySQL 8.0 中的高可用方案 2、适用场景 二、环境准备 1、系统环境说明…...

pycharm 新UI 固定菜单栏 pycharm2025 中文版

pycharm 新UI 文件 -> 设置 -> 外观与行为 -> 外观 -> UI选项 -> 主菜单:显示在主工具栏上方. 即可固定...

跟单业务和量化交易业务所涉及到的设计模式

&#x1f501; 跟单业务中常用的设计模式&#xff1a; 1. 观察者模式&#xff08;Observer&#xff09; 场景&#xff1a;一个大V下单&#xff0c;系统需要自动通知所有跟随者进行同步下单。好处&#xff1a;解耦下单者与跟随者&#xff0c;支持灵活扩展、异步通知。面试亮点…...

我的世界Java版1.21.4的Fabric模组开发教程(十一)创建方块

这是适用于Minecraft Java版1.21.4的Fabric模组开发系列教程专栏第十一章——创建方块。想要阅读其他内容&#xff0c;请查看或订阅上面的专栏。 方块(Block) 是构成Minecraft世界的主要组成部分&#xff0c;是组成游戏地图的最基本单元&#xff0c;也是模组开发的核心元素之一…...

VR/AR 视网膜级显示破局:10000PPI 如何终结颗粒感时代?

一、传统液晶 “纱窗效应”&#xff1a;VR 沉浸体验的最大绊脚石 当用户首次戴上 VR 头显时&#xff0c;眼前密密麻麻的像素网格往往打破沉浸感 —— 这正是传统液晶显示在近眼场景下的致命缺陷。受限于 500-600PPI 的像素密度&#xff0c;即使达到 4K 分辨率&#xff0c;等效到…...