【JavaEE 初阶】⽹络编程套接字
一、⽹络编程基础
操作系统提供的一组 api =>socket api(传输层给应用层提供)
- TCP
- UDP
因此, socket api 提供了两套
TCP 有连接, 可靠传输, 面向字节流, 全双工
UDP 无连接, 不可靠传输, 面向数据报, 全双工
要进行网络通信, 物理上的连接(网线啥的)
- 对于 TCP 来说, TCP 协议中,就保存了对端的信息
- A 和 B 通信, A 和 B 先建立连接
- 让 A 保存B 的信息,B 保存 A 的信息 (彼此之间知道,谁是和他建立连接的那个)
- 对于 UDP 来说, UDP 协议本身,不保存对方的信息 就是 无连接
网络上,数据是非常容易出现丢失的情况(丢包)
光信号/电信号,都可能受到外界的干扰
面向字节流, 读写数据的时候,是以字节为单位
面向数据报,读写数据的时候,以一个数据报为单位 (不是字符) 一次必须读写一份udp数据报,不能是半个
- 支持任意长度-->粘包问题
- 不存在粘包-->长度限制
一个通信链路,只支持单向通信(要么读,要么写)
1.为什么需要⽹络编程?
2.什么是⽹络编程
- 进程A:编程来获取⽹络资源
- 进程B:编程来提供⽹络资源
3.⽹络编程中的基本概念
(1)发送端和接收端
- 发送端:数据的发送⽅进程,称为发送端。发送端主机即⽹络通信中的源主机。
- 接收端:数据的接收⽅进程,称为接收端。接收端主机即⽹络通信中的⽬的主机。
- 收发端:发送端和接收端两端,也简称为收发端。
(2)请求和响应
- 第⼀次:请求数据的发送
- 第⼆次:响应数据的发送。
(3)客⼾端和服务端
- 服务端:在常⻅的⽹络数据传输场景下,把提供服务的⼀⽅进程,称为服务端,可以提供对外服务。
- 客⼾端:获取服务的⼀⽅进程,称为客⼾端。
- 客⼾端获取服务资源
- 客⼾端保存资源在服务端
- 银⾏提供存款服务:⽤⼾(客⼾端)保存资源(现⾦)在银⾏(服务端)
- 银⾏提供取款服务:⽤⼾(客⼾端)获取服务端资源(银⾏替⽤⼾保管的现⾦)
(4)常⻅的客⼾端服务端模型
- 客⼾端先发送请求到服务端
- 服务端根据请求数据,执⾏相应的业务处理
- 服务端返回响应:发送业务处理结果
- 客⼾端根据响应数据,展⽰处理结果(展⽰获取的资源,或提⽰保存资源的处理结果)
二、Socket套接字
1.概念
2.分类
- 流套接字:使⽤传输层TCP协议
- 有连接
- 可靠传输
- ⾯向字节流
- 有接收缓冲区,也有发送缓冲区
- ⼤⼩不限
- 数据报套接字:使⽤传输层UDP协议
- ⽆连接
- 不可靠传输
- ⾯向数据报
- 有接收缓冲区,⽆发送缓冲区
- ⼤⼩受限:⼀次最多传输64k
- 原始套接字
三、Java数据报套接字通信模型




- 客⼾端和服务端:开发时,经常是基于⼀个主机开启两个进程作为客⼾端和服务端,但真实的场景,⼀般都是不同主机。
- 注意⽬的IP和⽬的端⼝号,标识了⼀次数据传输时要发送数据的终点主机和进程
- Socket编程我们是使⽤流套接字和数据报套接字,基于传输层的TCP或UDP协议,但应⽤层协议, 也需要考虑,这块我们在后续来说明如何设计应⽤层协议。
- 关于端⼝被占⽤的问题 : 如果⼀个进程A已经绑定了⼀个端⼝,再启动⼀个进程B绑定该端⼝,就会报错,这种情况也叫端⼝被占⽤。

- 在cmd输⼊ netstat -ano | findstr 端⼝号 ,则可以显⽰对应进程的pid。如以下命 令显⽰了8888进程的pid
- 在任务管理器中,通过pid查找进程

- 如果占⽤端⼝的进程A不需要运⾏,就可以关闭A后,再启动需要绑定该端⼝的进程B
- 如果需要运⾏A进程,则可以修改进程B的绑定端⼝,换为其他没有使⽤的端⼝。
四、UDP数据报套接字编程
1.API 介绍
(1)DatagramSocket
方法签名 | 方法说明 |
DatagramSocket() | 创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于客户端) |
DatagramSocket(int port) | 创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端) |
方法签名 | 方法说明 |
void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待) |
void send(DatagramPacket p) | 从此套接字发送数据报包(不会阻塞等待,直接发送) |
void close() | 关闭此数据报套接字 |
(2)DatagramPacket
方法签名 | 方法说明 |
DatagramPacket(bytel] buf, int length) | 构造-个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length) |
DatagramPacket(bytel] buf, int offset, int length,SocketAddress address) | 构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length)。address指定目的主机的IP和端口号 |
方法签名 | 方法说明 |
InetAddress getAddress() | 从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址 |
int getPort() | 从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号 |
byte[] getData() | 获取数据报中的数据 |
(3)InetSocketAddress
方法签名 | 方法说明 |
InetSocketAddress(InetAddress addr, int port) | 创建⼀个Socket地址,包含IP地址和端⼝号 |
2.代码示例
(1)UdpEchoServer
创建socket对象
private DatagramSocket socket=null;
public UdpEchoServer(int port)throws SocketException{//指定固定端口号,使用服务器socket = new DatagramSocket(port);
}
socket 对象代表网卡文件.
读这个文件等于从网卡收数据, 写这个文件等于让网卡发数据
主循环
//1.读取请求并解析
//DatagramPacket表示一个udp数据报,此处传入的字节数组,相当于保存udp的载荷部分DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);
//输出型参数,事前调用空的对象,receive把它从网卡读入进行处理,填充参数socket.receive(requestPacket);
//把读取的二进制数据转换成字符串String request=new String(requestPacket.getData(),0, requestPacket.getLength());
a)构造 DatagramPacket 对象.
DatagramPacket 就代表 UDP 数据包.
报头 + 载荷(new 字节数组保存).b) 调用 receive .
理解输出型参数c)把 udp 数据包载荷取出来, 构造成一个 String
1)通过requestPacket,getData()拿到 DatagramPacket 中的字节数组
2)拿到有效数据的长度requestPacket .getLength()
3)根据字节数组,构造出一个 new String
//2.根据请求,计算响应(key)echo服务器,不需要计算响应,直接返回
String response=process(request);//后续如果需要进行服务器数据处理,可以采用单独改变此方法private String process(String request) {return request;}
//3.把相应返回给客户端
//不能使用response.length(),这个表示string中字符的个数
//response.getBytes().length,这个表示string中字节的个数
//requestPacket.getSocketAddress()获取报头部分的返回ip和端口号DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length
,requestPacket.getSocketAddress());
//此处不能直接发送,udp协议自身没有保存对方的信息(不知道发给谁)
//需要指定目的ip和目的端口,收到请求的源ip和源端口即所需socket.send(responsePacket);
1)response.getBytes() 拿到字符串中的字节数组 2)response.getBytes().length 拿到字节数组的长度 而不是使用字符串长度(单位 字符) 3)requestPacket.getSocketAddress() 这个方法返回的对象中同时包含 IP 和端口 4) new DatagramPacket 是要干啥?? 构造响应数据报,上面的是“请求数据报 5) socket.send(responsePacket); 把构造好的数据报发送出去前提是报头中包含了目的ip和目的端口
System.out.printf("[%s:%d]req:%s,resp:%s",requestPacket.getAddress().toString(),
requestPacket.getPort(),request,response);
Q:socket不用close吗?
A:文件要关闭,考虑清楚这个文件对象的 生命周期是怎样的
此处的 socket 对象, 伴随整个 udp 服务器, 自始至终
如果服务器关闭 (进程结束),进程结束时就会自动释放 PCB 的文件描述符表中的所有资源,也不需要手动调用 close 了.Q:此时没有发送请求or没有客户端,那么服务器程序此时应该怎么样呢?
A:应该在receive处阻塞等待
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpEchoServer {private DatagramSocket socket=null;public UdpEchoServer(int port)throws SocketException{//指定固定端口号,使用服务器socket = new DatagramSocket(port);}//启动服务器public void start() throws IOException {System.out.println("启动服务器");while(true){//循环一次相当于处理一次请求,处理请求为以下三个过程//1.读取请求并解析//DatagramPacket表示一个udp数据报,此处传入的字节数组,相当于保存udp的载荷部分DatagramPacket requestPacket=new DatagramPacket(new byte[4096],4096);//输出型参数,事前调用空的对象,receive把它从网卡读入进行处理,填充参数socket.receive(requestPacket);//把读取的二进制数据转换成字符串String request=new String(requestPacket.getData(),0, requestPacket.getLength());//2.根据请求,计算响应(key)echo服务器,不需要计算响应,直接返回String response=process(request);//3.把相应返回给客户端//不能使用response.length(),这个表示string中字符的个数//response.getBytes().length,这个表示string中字节的个数//requestPacket.getSocketAddress()获取报头部分的返回ip和端口号DatagramPacket responsePacket=new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());//此处不能直接发送,udp协议自身没有保存对方的信息(不知道发给谁)//需要指定目的ip和目的端口,收到请求的源ip和源端口即所需socket.send(responsePacket);//4.打印日志System.out.printf("[%s:%d]req:%s,resp:%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);}}//后续如果需要进行服务器数据处理,可以采用单独改变此方法public String process(String request) {return request;}public static void main(String[] args) throws IOException {UdpEchoServer server=new UdpEchoServer(9090);server.start();}
}
(2)UdpEchoClient
创建socket对象
private DatagramSocket socket=null;//和服务端不一样,还需要客户端地址,udp本身不保存对端信息,咱们自己保存一下
private String serverIp;
private int serverPort;public UdpEchoClient(String serverIp,int serverPort)throws SocketException {this.serverIp=serverIp;this.serverPort=serverPort;socket=new DatagramSocket();//一定不能写端口号,如果固定端口号,一旦该端口被用了,那么当被其他程序占用时,这个程序就会运行失效}
主循环
//1.从控制台读取用户输入的内容
System.out.println("请输入要发送的内容");
if(!scanner.hasNext()){break;}
String request = scanner.next();
//2.把请求发送给服务端,需要构造请求数据包
//构造时,不光要有载荷,也要有对应的端口和ipDatagramPacket requestPacket = new DatagramPacket(request.getBytes(),
request.getBytes().length,InetAddress.getByName(serverIp), serverPort);
//3.发送数据报
socket.send(requestPacket);
//4.接受服务器的回应
DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
socket.receive(responsePacket);
//将从服务器读取的数据进行解析,打印出来
String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
System.out.println(response);
整体程序
import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket=null;//和服务端不一样,还需要客户端地址,udp本身不保存对端信息,咱们自己保存一下private String serverIp;private int serverPort;public UdpEchoClient(String serverIp,int serverPort)throws SocketException {this.serverIp=serverIp;this.serverPort=serverPort;socket=new DatagramSocket();//一定不能写端口号,如果固定端口号,一旦该端口被用了,那么当被其他程序占用时,这个程序就会运行失效}public void start() throws IOException {Scanner scanner = new Scanner(System.in);while(true) {//1.从控制台读取用户输入的内容System.out.println("请输入要发送的内容");if(!scanner.hasNext()){break;}String request = scanner.next();//2.把请求发送给服务端,需要构造请求数据包//构造时,不光要有载荷,也要有对应的端口和ipDatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,InetAddress.getByName(serverIp), serverPort);//3.发送数据报socket.send(requestPacket);//4.接受服务器的回应DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);socket.receive(responsePacket);//将从服务器读取的数据进行解析,打印出来String response = new String(responsePacket.getData(), 0, responsePacket.getLength());System.out.println(response);}}public static void main(String[] args) throws IOException {UdpEchoClient client=new UdpEchoClient("127.0.0.1",9090);// 环回ip,表示当前主机,无论真实ip是什么,都可以用它替代,相当于thisclient.start();}}
(3)UDP Dict Server
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;public class UdpDictServer extends UdpEchoServer {private HashMap<String,String>dict=new HashMap<>();public UdpDictServer(int port) throws SocketException {super(port);//初始化词典dict.put("小狗","dog");dict.put("小猫","cat");dict.put("小鸭子","duck");dict.put("小兔子","rabbit");}@Overridepublic String process(String request){return dict.getOrDefault(request,"未找到该词条");}public static void main(String[] args) throws IOException {UdpDictServer dictServer=new UdpDictServer(9090);dictServer.start();}
}
五、TCP流套接字编程
1.API 介绍
(1)ServerSocket
方法签名 | 方法说明 |
ServerSocket(int port) | 创建一个服务端流套接字Socket,并绑定到指定端口 |
方法签名 | 方法说明 |
Socket accept() | 开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象, 并基于该Socket建立与客户端的连接,否则阻塞等待 |
void close() | 关闭此套接字 |
(2)Socket
方法签名 | 方法说明 |
Socket(String host, int port) | 创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接 |
方法签名 | 方法说明 |
InetAddress getlnetAddress() | 返回套接字所连接的地址 |
InputStream getinputStream() | 返回此套接字的输入流 |
OutputStream getOutputStream() | 返回此套接字的输出流 |
TCP Echo Server
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("启动服务器");//这种情况一般不会是用fixedThreadPool,意味着同时处理的客户端数目固定了ExecutorService executorService= Executors.newCachedThreadPool();while (true){//tcp要先处理客户端发来的连接//通过读写clientsocket和客户端进行通信//如果客户端没有发送消息,accept会阻塞//主线程负责accept,每次accept一个客户端,就创建一个线程,由新线程负责处理客户的请求Socket clientSocket= serversocket.accept();//使用多线程的方式来调整
// Thread thread=new Thread(()->{
// try {
// processConnect(clientSocket);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// });
// thread.start();//使用线程池来调整executorService.submit(()->{try {processConnect(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}});}}
//处理一个客户端的连接,可能会涉及多个客户端的连接和响应private void processConnect(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){//1.读取请求并解析,可以借助read,也可以借助scanner来辅助完成if(!scanner.hasNext()){System.out.printf("[%s,%d]客户端下线\n",clientSocket.getInetAddress(),clientSocket.getPort());break;}String request=scanner.next();//2.根据请求计算响应String response=process(request);//3.返回响应printWriter.println(response);printWriter.flush();//等价于 outputStream.write(response.getBytes());//4.写日志System.out.printf("[%s,%d]req:%s.resp:%s\n",clientSocket.getInetAddress(),clientSocket.getPort(),request,response);}} catch (IOException e) {throw new RuntimeException(e);}finally {clientSocket.close();}}private String process(String request) {return request;}public static void main(String[] args) throws IOException {TcpEchoServer tcpEchoServer=new TcpEchoServer(9090);tcpEchoServer.start();}
}
TCP Echo Client
import javax.imageio.IIOException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.SocketException;
import java.util.Scanner;public class TcpEchoClient {private Socket clientsocket=null;public TcpEchoClient(String SeverIp, int ServerPort)throws IOException {//直接把字符串的ip设置进来clientsocket=new Socket(SeverIp,ServerPort);}public void start(){Scanner scanner=new Scanner(System.in);try(InputStream inputStream=clientsocket.getInputStream();OutputStream outputStream=clientsocket.getOutputStream()) {//为了方便操作,套壳操作Scanner scannerNet=new Scanner(inputStream);PrintWriter printWriter=new PrintWriter(outputStream);while(true){//1.从控制台读取用户输入String request=scanner.next();//2.直接送给服务器printWriter.println(request);//这一步只是写到缓冲区里面,还要刷新一下才能发送printWriter.flush();//3.读取响应String response=scannerNet.next();//4.打印至控制台System.out.println(response);}} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) throws IOException {TcpEchoClient tcpEchoClient=new TcpEchoClient("127.0.0.1",9090);tcpEchoClient.start();}}
服务器引⼊多线程
//使用多线程的方式来调整
Thread thread=new Thread(()->{try {processConnect(clientSocket);} catch (IOException e) {throw new RuntimeException(e);}
});
thread.start();
服务器引⼊线程池
//使用线程池来调整
executorService.submit(()->{try {processConnect(clientSocket);
} catch (IOException e) {throw new RuntimeException(e);
}});
- 读写数据通过Socket,通过Socket内置的InputStream和 OutputStream,读写基本单位是字节
- 当前在编写客户端服务器的时候,是需要约定请求/响应之间的分隔符的.(\n)
- 服务器这边accept得到的socket对象,记得及时关闭
- 要处理多个客户端,需要搭配多线程/线程池
⻓短连接
- 短连接:每次接收到数据并返回响应后,都关闭连接,即是短连接。也就是说,短连接只能⼀次收发数据。
- ⻓连接:不关闭连接,⼀直保持连接状态,双⽅不停的收发数据,即是⻓连接。也就是说,⻓连接可以多次收发数据。
- 建⽴连接、关闭连接的耗时:短连接每次请求、响应都需要建⽴连接,关闭连接;⽽⻓连接只需要 第⼀次建⽴连接,之后的请求、响应都可以直接传输。相对来说建⽴连接,关闭连接也是要耗时的,⻓连接效率更⾼。
- 主动发送请求不同:短连接⼀般是客⼾端主动向服务端发送请求;⽽⻓连接可以是客⼾端主动发送请求,也可以是服务端主动发。
- 两者的使⽤场景有不同:短连接适⽤于客⼾端请求频率不⾼的场景,如浏览⽹⻚等。⻓连接适⽤于客⼾端与服务端通信频繁的场景,如聊天室,实时游戏等。
六、扩展了解
由于每个连接都需要不停的阻塞等待接收数据,所以每个连接都会在⼀个线程中运⾏。⼀次阻塞等待对应着⼀次请求、响应,不停处理也就是⻓连接的特性:⼀直不关闭连接,不停的处理请求。
相关文章:

【JavaEE 初阶】⽹络编程套接字
一、⽹络编程基础 1.应用层 操作系统提供的一组 api >socket api(传输层给应用层提供) 2.传输层 两个核心协议. TCPUDP 差别非常大,编写代码的时候,也是不同的风格 因此, socket api 提供了两套 TCP 有连接, 可靠传输, 面向字节流, 全双工 UDP …...
【Linux内核】Hello word程序
创建测试目录 mkdir -p ~/develop/kernel/hello-1 cd ~/develop/kernel/hello-1 创建MakeFile文件和内核.c文件 nano Makefile nano hello-1.c 编写内容 /* * hello-1.c - The simplest kernel module. */ #include <linux/module.h> /* Needed by all modules */…...
PHP 与 MySQL 搭配的优势
一、PHP 与 MySQL 搭配的优势 强大的动态网页开发能力 PHP 是一种服务器端脚本语言,能够生成动态网页内容。它可以根据用户的请求、数据库中的数据等因素,实时地生成 HTML 页面返回给客户端浏览器。而 MySQL 是一个流行的关系型数据库管理系统…...
深入浅出:PHP中的变量与常量全解析
文章目录 引言理解变量普通变量赋值操作变量间赋值引用赋值取消引用 可变变量预定义变量 理解常量声明常量使用define()函数const关键字 使用常量预定义常量 扩展话题:作用域与生命周期实战案例总结与展望参考资料 引言 在编程的世界里,变量和常量是两种…...

初步简单的理解什么是库,什么是静态库,什么是动态库
库是什么 库根据名字我们应该很容易理解,在我们日常生活种,包含库的东西有很多,像仓库,库房那些,库是拿来存放,方便管理东西的,在我们编程当中,库的定义也是如此 那么为什么要有库…...

从ctfwiki开始的pwn之旅 3.ret2syscall
ret2syscall 原理 ret2syscall,即控制程序执行系统调用,获取 shell。 那么ret2text——程序中有system("/bin/sh")代码段,控制流执行 那么ret2shellcode——程序中不存在system("/bin/sh/")的代码段,自己…...
使用 httputils + protostuff 实现高性能 rpc
1、先讲讲 protostuf protostuf 一直是高性能序列化的代表之一。但是用起来,可难受了,你得先申明 protostuf 配置文件,并且要把这个配置文件转成类。所以必然要学习新语法、新工具。 可能真的太难受了!于是乎,&#…...

系统思考—战略共识
最近与和一位企业创始人深度交流时,他告诉我:“虽然公司在制定战略时总是非常明确,但在执行过程中,经常发现不同层级对战略的理解偏差,甚至部分团队的执行效果与预期大相径庭。每次开会讨论时,大家都说得头…...

Java版-速通数据结构-树基础知识
现在面试问mysql,红黑树好像都是必备问题了。动不动就让手写红黑树或者简单介绍下红黑树。然而,我们如果直接去看红黑树,可能会一下子蒙了。在看红黑树之前,需要先了解下树的基础知识,从简单到复杂,看看红黑树是在什么…...

详尽的oracle sql函数
1,CHR 输入整数,返回对应字符。 用法:select chr(65),chr(78) from dual; 2,ASCII 输入字符,返回对应ASCII码。 用法:select ascii(A),ascii(B) from dual; 3,CONCAT 输入两个字符串,…...

SAP IDOC Error VG205
今天在做IDOC 入栈处理销售订单的时候,一直报错VG205 There is no article description for item 000030 这个问题在通过WE19 前台显示的时候就不会遇见, 只有在接口传输的时候才会遇到 搜索发现,可以通过配置忽略此消息号 配置路径如下…...
DSP 的 CV 算子调用
01 前言 DSP 是 征程 5 上的数字信号处理器,专用于处理视觉、图像等信息。在 OE 包的 ddk/samples/vdsp_rpc_sample 路径下,提供了 DSP 使用示例,包括 nn 和 CV 两部分。 nn 示例涵盖了深度学习模型的相关算子,包括量化、反量化、…...

WMI攻击-基础篇(一)
#WMI攻击-基础篇(一) 这篇文章是关于WMI攻击系列文章的第一部分,面向新手。如果对Powershell有一定了解会对阅读本文有所帮助,但这并不是必需的,我们直接上干货。 #1、概述 为什么是WMI? WMI 是 Microso…...
使用Pygame创建一个简单的消消乐游戏
消消乐游戏是一种经典的益智游戏,玩家通过交换相邻的方块来形成三个或更多相同颜色的连续方块,从而消除它们。本文将介绍如何使用Python的Pygame库来创建一个简单的消消乐游戏。 准备工作 在开始之前,请确保已安装Pygame库。可以通过以下命…...
证明直纹面是可展曲面沿着直母线,曲面的切平面不变
目录 证明直纹面是可展曲面的当且仅当沿着直母线,曲面的切平面不变 证明直纹面是可展曲面的当且仅当沿着直母线,曲面的切平面不变 直纹面是可展曲面当且仅当沿着直母线,曲面的切平面不变. 证明:设直纹面 S S S的参数式为 r ( u …...

Chrome控制台 网站性能优化指标一览
打开chrome-》f12/右键查看元素-》NetWrok/网络 ctrlF5 刷新网页,可以看到从输入url到页面资源请求并加载网页,用于查看资源加载,接口请求,评估网页、网站性能等,如下图: request、stransferred、resour…...
Typora创建markdwon文件的基础语法
标题的创建 使用#空格xxx 可使xxx为标题,同时第一标题为#空格标题;第二标题为##空格标题2。以此类推最多可创建六个标题。 同时按住Ctrl1可创建第一标题,同时按住Ctrl2可创建第二标题,以此类推,最多可创建六个标题。也…...
《嵌入式硬件设计》
一、引言 嵌入式系统在现代科技中占据着至关重要的地位,广泛应用于消费电子、工业控制、汽车电子、医疗设备等众多领域。嵌入式硬件设计作为嵌入式系统开发的基础,直接决定了系统的性能、可靠性和成本。本文将深入探讨嵌入式硬件设计的各个方面ÿ…...
【AIGC】大模型面试高频考点-位置编码篇
【AIGC】大模型面试高频考点-位置编码篇 (一)手撕 绝对位置编码 算法(二)手撕 可学习位置编码 算法(三)手撕 相对位置编码 算法(四)手撕 Rope 算法(旋转位置编码…...
如何使用 SQL 语句创建一个 MySQL 数据库的表,以及对应的 XML 文件和 Mapper 文件
文章目录 1、SQL 脚本语句2、XML 文件3、Mapper 文件4、启动 ServiceInit 文件5、DataService 文件6、ComplianceDBConfig 配置文件 这个方式通常是放在项目代码中,使用配置在项目的启动时创建表格,SQL 语句放到一个 XML 文件中。在Spring 项目启动时&am…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...

短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
Git常用命令完全指南:从入门到精通
Git常用命令完全指南:从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...