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

Java网络编程实战:TCP/UDP Socket通信详解与高并发服务器设计

在这里插入图片描述

🔍 开发者资源导航 🔍
🏷️ 博客主页: 个人主页
📚 专栏订阅: JavaEE全栈专栏

内容:

    • socket(套接字)
    • TCP和UDP差别
    • UDP编程
      • 方法
      • 使用
      • 简单服务器实现
    • TCP编程
      • 方法
      • Socket和ServerSocket之间的关系
      • 使用
      • 简单服务器实现
      • 多线程优化

socket(套接字)

在网络协议中,应用层是和程序员直接相关的,涉及到的网络通信协议也大多是程序员自定制的。

而在操作系统中提供了一组API:socket api(传输层给应用层提供的),socket相当于网卡的遥控器,我们可以通过这组api来间接操作网卡。

socket主要提供了两套,一个是TCP的,另一个是UDP,这两个核心协议差别很大,编写的时候风格不同,因此socket api提供了两套。

TCP和UDP差别

TCP:有连接,可靠传输,面向字节流,全双工
UDP:无连接,不可靠传输,面向数据报,全双工

  • 有连接 & 无连接: 对于TCP来说,TCP协议保存了对端的信息,如果A和B进行通信,A保存B的信息,B也保存A的信息,彼此都知道谁是和它建立联系的。而UDP就不会存储彼此的信息。

  • 可靠传输 & 不可靠传输: 在网络中,数据非常容易出现丢包的情况,因为光信号和电信号可能会收到外界的干扰,本来传输的数据被修改了,这样乱的数据会被丢弃掉。而可靠传输并不是保证数据包100%到达,而是尽可能的提高传输成功率,如果丢包了,也可以感知到,而可靠传输的代价就是效率降低~不可靠传输,只是把数据发送出去了,其他的不管了。

  • 面向字节流 & 面向数据报: TCP读写数据时是以字节为单位的,长度是任意的,可能会出现“粘包”问题。UPD读写数据时以一个数据报为单位,一次必须是一整个不能是半个,严格收到长度的限制,因此不会出现“粘包”问题。

  • 双全工 & 半全工: 一个通信链路层如果支持双向通信则称为双全工,如果只支持单向通信(仅能读或者写)则称之为半全工

粘包(TCP Stickiness)是指 TCP 协议在传输数据时,多个数据包被接收方当作一个包接收,导致数据解析错误的现象。主要发生在 基于流的传输协议(如 TCP),而 UDP 不会粘包,因为 UDP 是面向消息的协议,每个数据包都有明确的边界。

UDP编程

方法

DatagramSocket是JAVA为UPD提供的实现类。

构造方法:

方法用途
DatagramSocket()构建一个数据报套接字绑定到本地主机的任何可用的端口。
DatagramSocket(int port)构建一个数据报套接字绑定到本地主机的指定端口。

核心方法:

方法用途
send(DatagramPacket p)从该套接字发送数据报包。
receive(DatagramPacket p)从该套接字接收数据报包。
close()关闭该数据报套接字。

上述我们讲到UDP是以数据报为一个单位进行读写的,而在JAVA中DatagramPacket类表示一个完整的UDP数据报。

构造方法:

方法用途
DatagramPacket(byte[] buf, int length) 接收数据包长度 length 构建。
public DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)发送有偏置 offset指定端口号指定主机上的数据包长度 length数据报包结构。

核心方法:

方法用途
getData() 返回数据缓冲区。
getLength() 返回要发送的数据的长度或收到的数据的长度。
getAddress() 返回的IP地址的机器,这个数据包被发送或从收到的数据报。
getSocketAddress() 获取SocketAddress(通常是IP地址+端口号)的远程主机,数据包被发送到或来自。

使用

构造数据报

DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
  1. 第一个参数:创建一个长度为1024字节的字节数组
    这个数组将作为数据包的缓冲区,用于存储接收到的网络数据
  2. 第二个参数:1024
    指定缓冲区的大小(这里是1024字节)
    表示这个数据包最多能接收1024字节的数据

看到这里不知道你是否和我一样好奇,这里为什么要这样设计呢???这里的数值反正都一样,直接传入第一个参数不就可以获得第二个参数吗。

  • 当接收长度 < 缓冲区长度时 即使数据包大于1024字节,也只会填充缓冲区的前1024字节,可以限制接收数据量,防止缓冲区溢出
  • 当接收长度 > 缓冲区长度时 会抛出IllegalArgumentException异常

推荐做法:使接收长度等于缓冲区长度
特殊场景:当只需要接收部分数据时可以使用不一致设置

接受数据报

DatagramSocket socket =  new DatagramSocket(9090);
//存储字节大小为1024字节数组。
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
//接收数据
socket.receive(packet);
//将有效部分转化成字符串
String receive = new String(packet.getData(), 0, packet.getLength());
System.out.println("收到消息:" + receive);

getLength()方法是获取DatagramPacket 的length属性,也就是在初始时设置的1024,但是length在接受到数据报后会被改变,变成实际接收到的字节长度。

发送数据报

DatagramSocket socket =  new DatagramSocket(9090);
//存储字节大小为1024字节数组。
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
//接收数据
socket.receive(packet);
String respose  = "你好呀";
DatagramPacket resposePacket = new DatagramPacket(respose.getBytes(), respose.getBytes().length, packet.getSocketAddress());
socket.send(resposePacket);

在这里我们将信息重新发回源地址,那么怎么知道对方的ip呢?我们接收到的数据报里面含有对方的源端口以及源ip地址,那么此时对方的源地址就是我们的目的地址,而我们的getSocketAddress()方法恰好可以返回一个带有端口以及ip地址的类。

除此之外,还要注意我们的respose.getBytes().length,这里不能使用respose.length,因为字符的长度不等于字节的长度!

简单服务器实现

如果你理解了上述的代码,那么你就可以完成一个简单的回显服务器啦~
所谓回显服务器就是你的询问是什么,回答的就是什么…

一个服务器程序通常的流程:

  1. 读取请求并解析
  2. 根据请求,计算响应。(服务器最关键的部分)
  3. 返回响应
客户端输入消息
创建DatagramPacket
发送到服务端
服务端接收packet
处理请求
创建响应packet
返回客户端
客户端显示响应

服务端代码:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.util.Scanner;public class UdpechoServer {//服务端DatagramSocket socket = null;public UdpechoServer (int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("服务器启动了");while (true) {//存储字节大小为1024字节数组。DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);//接收数据socket.receive(packet);//将有效部分转化成字符串String receive = new String(packet.getData(), 0, packet.getLength());System.out.println("收到消息:" + receive);//根据输入回复String respose = poss(receive);//构建响应的数据报。并且传入目的端口和地址DatagramPacket resposePacket = new DatagramPacket(respose.getBytes(), respose.getBytes().length, packet.getSocketAddress());socket.send(resposePacket);//打印日志System.out.printf("[%s:%d] req:%s resp:%s\n", packet.getAddress(), packet.getPort(), receive, respose);}}//处理请求的函数,根据需要进行调整;private String poss(String receive) {return receive;}public static void main(String[] args) throws IOException {UdpechoServer udpechoServer = new UdpechoServer(9090);udpechoServer.start();}
}

关键点解析:

  1. 使用while(ture)是否会出现忙等问题?

当调用 socket.receive() 时,如果内核的 接收缓冲区(Receive Buffer) 中没有数据,应用程序的线程会被操作系统挂起(进入阻塞状态),并添加到该 Socket 的等待队列中。此时线程不会占用 CPU。。

  1. 服务端的端口号如何设置?

服务端的端口号一般设置为一个固定值,这样服务端相当于一个固定的商户,而不是流动的小摊,客户可以根据这个地址一直找到你,如果你的地址一直在变化,客户怎么找到你?你总不能一直让客户去找你吧?

  1. DatagramSocket 既然有close方法,是否需要关闭?

是否需要关闭需要考虑它的生命周期是怎样的,服务器一般是7*24小时的运行的,只要你开着它你就需要保持DatagramSocket 的开启状态,而如果你的服务器关闭了,进程结束时自然会关闭所有的资源,无需手动结束。

客户端代码:

import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {//udp不会保存对方的ip和端口号,因此需要额外进行保存private DatagramSocket socket = null;private String ServerAdress = null;private  int ServerPort;public UdpEchoClient(String adress, int port) throws SocketException {this.ServerPort = port;this.ServerAdress = adress;//客户端的端口号随机生成一个空闲的就行。socket = new DatagramSocket();}public void start() throws IOException {while (true) {Scanner sc = new Scanner(System.in);System.out.print("请输入:");String input = sc.nextLine();//getByName是一个特殊的构造方法DatagramPacket packet = new DatagramPacket(input.getBytes(), input.getBytes().length,InetAddress.getByName(ServerAdress), ServerPort);socket.send(packet);//等待回复System.out.println("等待回复中...");DatagramPacket respose = new DatagramPacket(new byte[1024], 1024);socket.receive(respose);String s = new String(respose.getData(), 0, respose.getLength());System.out.println("回复:" + s);}}public static void main(String[] args) throws IOException {UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);client.start();}
}

关键解析:

  1. 客户端如何设置端口号以及ip地址?

此处的ip地址127.0.0.1是指向的本机,毕竟咱是在本地上部署的嘛~,因此服务器的ip地址就是本机。
客户端端口号不建议使用固定值,因为咱无法控制客户端的电脑,万一这个端口号被占用了呢?因此只需要随机找一个空闲的端口号即可。

2.这里的DatagramSocket 是否需要关闭?

按理说客户端的是需要关闭的,但是咱这里用的while(ture),因此就没这个必要了。

在这里插入图片描述

运行结果
一定要先启动服务端再启动客户端!

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

TCP编程

方法

在JAVA中TCP协议使用ServerSocket创建一个服务端流套接字,并绑定到指定端口,作用:揽客,并把它交给Socket。

构造方法

方法定义说明
ServerSocket()创建一个绑定服务器套接字。
ServerSocket(int port)创建一个服务器套接字,绑定到指定的端口。

核心方法

方法定义说明
accept()监听要对这个套接字作出的连接并接受它。
close()关闭这个套接字。

因为TCP协议是通过字节流进行读写的,因此在JAVA中有一个专门的类Socket来实现这个读写功能,但是和UDP不同的是他一次可能会接收到多个请求。

构造方法

方法定义说明
Socket()创建一个连接的套接字,与socketimpl系统默认的类型。
Socket(InetAddress address, int port)创建一个流套接字,并将其与指定的IP地址中的指定端口号连接起来。

核心方法

方法定义说明
close()关闭这个套接字。
getPort()返回此套接字连接的远程端口号。
getOutputStream()返回此套接字的输出流。
getInputStream()返回此套接字的输入流。

Socket和ServerSocket之间的关系

他们之间和UDP的关系不同,UDP的客户端和服务端都需要一个DatagramPacket来进行发送和接受数据。

而在TCP中,ServerSocket仅用于服务端的建立连接,发送和接受数据是通过Socket的字节流来进行的。

特性Socket (客户端)ServerSocket (服务端)
作用主动连接服务端监听端口,接受客户端连接
连接方式直接连接指定IP和端口被动等待客户端连接
数据流双向通信不直接处理数据,仅管理连接

在这里插入图片描述

使用

接受信息

//服务端接受信息
ServerSocket Serversocket = new ServerSocket(9090);
Socket socket = Serversocket.accept();try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {//简化输入输出Scanner sc = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {if (!sc.hasNext()) {break;}//获取输入String receive = sc.next();//处理数据String respose = "hello";//读写缓冲区printWriter.println(respose);//刷新缓冲区printWriter.flush();}} catch (IOException e) {throw new RuntimeException(e);} finally {socket.close();}

相比于UDP的发送与接收,TCP的流程要复杂的多。

讲解点1:读写数据

我们知道他们是通过字节流来进行读写的,因此我们在建立连接以后,只需要通过他们的输入输出流来进行操作,输入流是接受对方发送的数据,输出流是主动给对方发送数据。

此外我们不知道对方什么时候结束连接,因此需要我们使用while(ture)来一直进行判断。

讲解点2:try-resource-close

字节流使用完是需要进行关闭的,使用try()包裹内部的资源它在结束后会自动关闭,避免忘记手动关闭。

讲解点3:Scanner & PrintWriter

使用这两个类对输入输出流进行封装可以简化我们的读取操作,并且PrintWriter 的println方法在输出的时候还会帮我们加上\n,可以有效分割多次输入或输出的差别。

讲解点4:刷新缓冲区flush

当我们使用PrintWriter 的时候会出现一个问题,println仅仅是将数据发送了缓冲区内,并没有真正写入网卡中,这是因为JAVA在设计它的时候为了保证它的高效性,想让用户尽可能的多写入几次,再一起发送出去。

但是在这里它会造成我们的数据并没有实际发送出去,因此我们需要增添一个flush方法冲刷缓冲区到网卡中去。

讲解点5:长连接 & 短连接

所谓短连接就是一次连接只发送一次请求,长连接则是可以有多个请求,而TCP的连接明显是长连接的,建立一次连接客户端可以一直发送请求,直到对方断开连接。

发送信息

//需要传入地址和端口号
Socket socket = new Socket("127.0.0.1", 9090);
Scanner sc = new Scanner(System.in);
try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {//简化输入输出Scanner scnet = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {String request = sc.next();//发送读入的数据printWriter.println(request);printWriter.flush();//返回的响应数据String respose = scnet.next();System.out.println(respose);}
} catch (IOException e) {throw new RuntimeException(e);
}

如果上一个理解了的话,这段代码应该不难理解,在这里的printWriter也是使用的println方法,这样可以约定双方的每一个请求以换行结束。

简单服务器实现

这里我们依然是一个回显服务器。

客户端Socket 服务端ServerSocket 服务端SocketA 启动服务端 new ServerSocket(9090) accept() [阻塞] 客户端连接 连接请求(SYN) 创建专属SocketA 连接确认(ACK) 数据通信阶段 发送数据(OutputStream) 返回数据(InputStream) 客户端Socket 服务端ServerSocket 服务端SocketA

服务端代码:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;public class TcpEchoServer {ServerSocket Serversocket = null;TcpEchoServer (int port) throws IOException {Serversocket = new ServerSocket(port);}public void start() throws IOException {System.out.println("服务器启动了~");ExecutorService executorService = Executors.newCachedThreadPool();while (true) {Socket socket = Serversocket.accept();executorService.submit(()->{try {processConnection(socket);} catch (IOException e) {throw new RuntimeException(e);}});}}private void processConnection(Socket socket) throws IOException {System.out.printf("[%s:%d]客户端上线!\n", socket.getInetAddress(), socket.getPort());try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {//简化输入输出Scanner sc = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {if (!sc.hasNext()) {System.out.printf("[%s:%d]客户端下线!\n", socket.getInetAddress(), socket.getPort());break;}//获取输入String receive = sc.next();
//                System.out.println("yes");//处理数据String respose = pross(receive);//读写缓冲区printWriter.println(respose);//刷新缓冲区printWriter.flush();//输出日志System.out.printf("[%s:%d] req:%s rep:%s\n",socket.getInetAddress(), socket.getPort(), receive, respose);}} catch (IOException e) {throw new RuntimeException(e);} finally {socket.close();}}private String pross(String receive) {return receive;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer = new TcpEchoServer(9091);tcpEchoServer.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 {Socket socket = null;public TcpEchoclient(String adress, int port) throws IOException {socket = new Socket(adress, port);}public void start() {//用户输入内容Scanner sc = new Scanner(System.in);try (InputStream inputStream = socket.getInputStream();OutputStream outputStream = socket.getOutputStream()) {//简化输入输出Scanner scnet = new Scanner(inputStream);PrintWriter printWriter = new PrintWriter(outputStream);while (true) {String request = sc.next();//发送读入的数据printWriter.println(request);printWriter.flush();//返回的响应数据String respose = scnet.next();System.out.println(respose);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoclient echoclient = new TcpEchoclient("127.0.0.1", 9091);echoclient.start();}
}

关键解析

  1. 是否会出现忙等问题

这里的accept方法和UPD的类似,没有信息时也会进入阻塞等待。
而在另一个循环里面sc.hasNext()如果输入流中没有数据,但连接未关闭,hasNext() 也会阻塞(线程挂起),直到数据到达或流关闭。

  1. 粘包问题

上述我们反复提到过TCP的这个问题,这里我们是通过println的换行符来对多个请求进行了分割。

除此之外,一点要记得关闭socket对象哦~

运行结果
运行顺序:先服务端再客户端。
在这里插入图片描述
在这里插入图片描述

多线程优化

通过这个代码不知道你有没有发现他并不支持多线程的操作?因为在服务端的代码里面它一次只能进行一次连接,只有这个连接断开的时候其他连接才有机会。

因此我们可以使用多线程的方法来解决这个问题:

public void start() throws IOException {System.out.println("服务器启动了~");while (true) {Socket socket = Serversocket.accept();Thread thread = new Thread(()->{try {processConnection(socket);} catch (IOException e) {throw new RuntimeException(e);}});thread.start();}
}

使用多线程操作,每个客户端都独享一个线程。

但是每次都创建和销毁这个线程开销会比较大,因此我们可以在起基础上再使用线程池进行优化。

public void start() throws IOException {System.out.println("服务器启动了~");ExecutorService executorService = Executors.newCachedThreadPool();while (true) {Socket socket = Serversocket.accept();executorService.submit(()->{try {processConnection(socket);} catch (IOException e) {throw new RuntimeException(e);}});}
}

如果客户端进一步增加达到1w或者100w,此时多线程/线程池,就会产生大量的线程,因此之后我们可以使用IO多路复用的方法来处理,感兴趣的可以自行了解。


感谢各位的观看Thanks♪(・ω・)ノ,创作不易,如果觉得有收获的话留个关注再走吧。

相关文章:

Java网络编程实战:TCP/UDP Socket通信详解与高并发服务器设计

&#x1f50d; 开发者资源导航 &#x1f50d;&#x1f3f7;️ 博客主页&#xff1a; 个人主页&#x1f4da; 专栏订阅&#xff1a; JavaEE全栈专栏 内容&#xff1a; socket(套接字)TCP和UDP差别UDP编程方法使用简单服务器实现 TCP编程方法Socket和ServerSocket之间的关系使用简…...

vue+threeJs 绘制3D圆形

嗨&#xff0c;我是小路。今天主要和大家分享的主题是“vuethreeJs 绘制圆形”。 今天找到一个用three.js绘制图形的项目&#xff0c;主要是用来绘制各种形状。 项目案例示意图 1.THREE.ShapeGeometry 定义&#xff1a;是 Three.js 中用于从 2D 路径形状&#xff08…...

Silky-CTF: 0x02靶场

Silky-CTF: 0x02 来自 <Silky-CTF: 0x02 ~ VulnHub> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.128&#xff0c;靶场IP192.168.23.131 3&#xff0c;对靶机进…...

Kafka 的优势是什么?

Kafka 作为分布式流处理平台的核心组件&#xff0c;其设计哲学围绕高吞吐、低延迟、高可扩展性展开&#xff0c;在实时数据管道和大数据生态中具有不可替代的地位。 一、超高吞吐量与低延迟 1. 磁盘顺序 I/O 优化 突破磁盘瓶颈&#xff1a;Kafka 将消息持久化到磁盘&#xff…...

基于FPGA + JESD204B协议+高速ADC数据采集系统设计

摘 要&#xff1a; 针对激光扫描共聚焦显微镜的大视场、高分辨率需求&#xff0c;为在振镜扫描的时间内获取更多数据量&#xff0c;设计一种基 于 FPGA 的高速数据采集系统。该系统采用 Xilinx 的 A7 系列 FPGA 作为主控芯片&#xff0c;同时选用 TI 公司提供的 LM…...

微服务中引入公共拦截器

本文使用的微服务版本为springcloudAlbaba :2021.0.4.0 微服务工程&#xff0c;一般公共的东西都放入一个工程&#xff0c;别的微服务都会引入这个工程&#xff0c;比如common-service,那么就可以在这个工程编写一个拦截器&#xff1a;&#xff0c;比如&#xff1a; public cla…...

Ubuntu20.04 LTS 升级Ubuntu22.04LTS 依赖错误 系统崩溃重装 Ubuntu22.04 LTS

服务器系统为PowerEdge R740 BIOS Version 2.10.2 DELL EMC 1、关机 开机时连续按键盘F2 2、System Setup选择第一个 System BIOS 3、System BIOS Setting 选择 Boot Setting 4、System BIOS Setting-Boot Setting 选择 BIOS Boot Settings 5、重启 开启时连续按键盘F11 …...

C++11:unique_ptr的基本用法、使用场景和最佳使用指南

文章目录 1. 简介2. 基本语法和用法2.1. 创建unique_ptr2.2. 访问指向的对象2.3. 所有权管理 3. 自定义删除器4. 数组支持5. 常见使用场景5.1. RAII资源管理5.2. 工厂模式5.3. 容器中存储多态对象5.4. Pimpl&#xff08;指针到实现&#xff09;习惯用法 6. 与其他智能指针的比较…...

测量3D翼片的距离与角度

1&#xff0c;目的。 测量3D翼片的距离与角度。说明&#xff1a; 标注A 红色框选的区域即为翼片&#xff0c;本示例的3D 对象共有3个翼片待测。L1与L2的距离、L1与L2的角度即为所求的翼片距离与角度。 2&#xff0c;原理。 使用线结构光模型&#xff08;标定模式&#xff0…...

零基础学习计算机网络编程----socket实现UDP协议

本章将会详细的介绍如何使用 socket 实现 UDP 协议的传送数据。有了前面基础知识的铺垫。对于本章的理解将会变得简单。将会从基础的 Serve 的初始化&#xff0c;进阶到 Client 的初始化&#xff0c;以及 run。最后实现一个简陋的小型的网络聊天室。 目录 1.UdpSever.h 1.1 构造…...

谷歌地图2022高清卫星地图手机版v10.38.2 安卓版 - 前端工具导航

谷歌地图2022高清卫星地图手机版是由谷歌公司推出的一款非常好用的手机地图服务软件&#xff0c;用户能够通过精准的导航和定位来查看地图&#xff0c;周边的商店等生活服务都会在地图上显示&#xff0c;用起来超级方便。 谷歌卫星高清地图 下载链接&#xff1a;夸克网盘分享 …...

RAG的ETL Pipeline源码解读

原文链接&#xff1a;SpringAI(GA)&#xff1a;RAG下的ETL源码解读 教程说明 说明&#xff1a;本教程将采用2025年5月20日正式的GA版&#xff0c;给出如下内容 核心功能模块的快速上手教程核心功能模块的源码级解读Spring ai alibaba增强的快速上手教程 源码级解读 版本&a…...

杭州白塔岭画室怎么样?和燕壹画室哪个好?

杭州作为全国美术艺考集训的核心区域&#xff0c;汇聚了众多实力强劲的画室&#xff0c;其中白塔岭画室和燕壹画室备受美术生关注。对于怀揣艺术梦想的考生而言&#xff0c;选择一所契合自身需求的画室&#xff0c;对未来的艺术之路影响深远。接下来&#xff0c;我们将从多个维…...

Linux文件系统:从VFS到Ext4的奇幻之旅

Linux文件系统&#xff1a;从VFS到Ext4的奇幻之旅 从虚拟文件到物理磁盘的魔法桥梁 引言&#xff1a;数据宇宙的"时空管理者" 当你在Linux终端输入ls -l时&#xff0c;一场跨越多个抽象层的精密协作悄然展开。文件系统作为操作系统中最复杂且最精妙的子系统之一&…...

5月底 端午节

感觉五月写的很少啊&#xff0c;尤其是这一周&#xff0c;真的事情特别多可能。但是实际上我晚上回宿舍之后大概九点十点这块&#xff0c;最后睡觉一般在十一点半到十二点。这一段时间我基本上都是浪费了。要么在打游戏要么在刷视频。但是最基本的生活保障和学习都没有做好。。…...

为何选择Spring框架学习设计模式与编码技巧?

&#x1f4cc; 结论先行 推荐项目&#xff1a;Spring Framework 推荐理由&#xff1a;设计模式覆盖全面 编码技巧教科书级实现 Java 生态基石地位 &#x1f3c6; 三维度对比分析 维度SpringMyBatisXXL-JOB设计模式⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐代码抽象⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐生态价…...

软件评测师 综合测试 真题笔记

计算机组成原理 用作科学计算为主的计算机&#xff0c;其对主机的运算速度要求很高,应该重点考虑 CPU的主频和字长&#xff0c;以及内存容量&#xff1b; 用作大型数据库处理为主的计算机&#xff0c;其对主机的内存容量、存取速度和外存储器的读写速度要求较高&#xff1b; 对…...

晶台光耦在手机PD快充上的应用

光耦&#xff08;光电隔离器&#xff09;作为关键电子元件&#xff0c;在手机PD快充中扮演信号隔离与传输的“安全卫士”。其通过光信号实现电气隔离&#xff0c;保护手机电路免受高电压损害&#xff0c;同时支持实时信号反馈&#xff0c;优化充电效率。 晶台品牌推出KL817、KL…...

JS对数据类型的检测

typeof对基本数据类型有用&#xff0c;但是对引用数据类型不行 console.log(typeof 2)//number console.log(typeof [])//object 失效 instanceof只对引用数据类型有用 console.log([] instanceof Array) //true console.log(2 instanceof String) //false constructor基本…...

llama.cpp:纯 C/C++ 实现的大语言模型推理引擎详解一

&#x1f680; llama.cpp&#xff1a;纯 C/C 实现的大语言模型推理引擎详解 一、什么是 llama.cpp&#xff1f; llama.cpp 是一个由 Georgi Gerganov 开源的项目&#xff0c;旨在使用纯 C/C 在 CPU 上运行 Meta 的 LLaMA 系列大语言模型。 它通过量化、优化注意力机制和内存…...

【亲测有效 | Cursor Pro每月500次快速请求扩5倍】(Windows版)Cursor中集成interactive-feedback-mcp

前言&#xff1a;使用这个interactive-feedback-mcp组件可以根据用户反馈来决定是否结束这一次的请求。如果本次请求并没有解决我们的问题&#xff0c;那我们便可以选择继续这次请求流程&#xff0c;直到问题解决。这样的话&#xff0c;就可以避免为了修复bug而白白多出的请求。…...

BaseTypeHandler用法-笔记

1.BaseTypeHandler简介 org.apache.ibatis.type.BaseTypeHandler 是 MyBatis 提供的一个抽象类&#xff0c;通过继承该类并实现关键方法&#xff0c;可用于实现 Java 类型 与 JDBC 类型 之间的双向转换。当数据库字段类型与 Java 对象属性类型不一致时&#xff08;如&#xff…...

鸿蒙OSUniApp集成WebGL:打造跨平台3D视觉盛宴#三方框架 #Uniapp

UniApp集成WebGL&#xff1a;打造跨平台3D视觉盛宴 在移动应用开发日新月异的今天&#xff0c;3D视觉效果已经成为提升用户体验的重要手段。本文将深入探讨如何在UniApp中集成WebGL技术&#xff0c;实现炫酷的3D特效&#xff0c;并特别关注鸿蒙系统(HarmonyOS)的适配与优化。 …...

华为盘古 Ultra MoE 模型:国产 AI 的技术突破与行业影响

2025 年 5 月 30日&#xff0c;华为正式发布参数规模达 7180 亿的盘古 Ultra MoE 模型&#xff0c;全程基于昇腾 AI 计算平台完成训练。这一进展标志着中国在超大规模人工智能模型领域的自主研发能力达到新高度&#xff0c;同时也为全球 AI 技术发展提供了新的技术路径。 盘古 …...

Payload CMS:开发者优先的Next.js原生开源解决方案,重新定义无头内容管理

在无头内容管理系统&#xff08;CMS&#xff09;竞争激烈的今天&#xff0c;Payload CMS凭借其独特的开发理念和技术架构迅速崛起&#xff0c;成为Microsoft、ASICS、Blue Origin等创新企业的选择。这款基于Node.js与TypeScript构建的开源解决方案&#xff0c;正在彻底改变开发…...

CRM管理软件的数据可视化功能使用技巧:让数据驱动决策

在当今数据驱动的商业环境中&#xff0c;CRM管理系统的数据可视化功能已成为企业优化客户管理、提升销售效率的核心工具。据企销客研究显示&#xff0c;具备优秀可视化能力的CRM系统&#xff0c;用户决策效率可提升47%。本文将深入解析如何通过数据可视化功能最大化CRM管理软件…...

linux批量创建文件

文章目录 批量创建空文件touch命令批量创建空文件循环结构创建 创建含内容文件echo重定向多行内容写入 按日期创建日志文件根据文件中的列内容&#xff0c;创建文件一行只有一列内容一行有多列内容 批量创建空文件 touch命令批量创建空文件 # 创建文件file1.txt到file10.txt …...

颠覆传统!单样本熵最小化如何重塑大语言模型训练范式?

颠覆传统&#xff01;单样本熵最小化如何重塑大语言模型训练范式&#xff1f; 大语言模型&#xff08;LLM&#xff09;的训练往往依赖大量标注数据与复杂奖励设计&#xff0c;但最新研究发现&#xff0c;仅用1条无标注数据和10步优化的熵最小化&#xff08;EM&#xff09;方法…...

华为数据之道 精读——【173页】读书笔记【附全文阅读】

在数字化浪潮中,企业数据管理的优劣直接关乎竞争力。华为凭借丰富实践经验总结的《华为数据之道》,为企业提供了全面且深入的数据治理方案。 笔记聚焦数字化转型与数据治理的紧密联系。华为作为非数字原生企业,在转型过程中克服了产业链条长、数据复杂等诸多难题,其…...

数据库OCP专业认证培训

认证简介 OCP 即 Oracle 数据库认证专家&#xff08;Oracle Certified Professional&#xff09;&#xff0c;是 Oracle 公司的 Oracle 数据库 DBA&#xff08;Database Administrator 数据库管理员&#xff09;认证课程。通过该认证&#xff0c;表明持证人能够管理大型数据库…...