【JavaEE初阶 — 网络编程】Socket 套接字 & UDP数据报套接字编程

      c96f743646e841f8bb30b2d242197f2f.gif

ddb5ae16fc92401ea95b48766cb03d96.jpeg692a78aa0ec843629a817408c97a8b84.gif


    1. Socket套接字    


    1.1 概念    


Socket 套接字,是由系统提供用于网络通信的技术,是基于TCP / IP协议的网络通信的基本操作单元。基于 Socket 套接字的网络程序开发就是网络编程。


    1.2 分类     


Socket套接字主要针对传输层协议划分为如下三类:


  • 对于字节流来说,可以简单的理解为,传输数据是基于IO流,流式数据的特征就是在IO流没有关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。 

  • 对于数据报来说,可以简单的理解为,传输数据是一块一块的,发送一块数据假如100个字节,必须一次发送,接收也必须一次接收100个字节,而不能分100次,每次接收1个字节。

     流套接字和数据报套接字的特点     

流套接字数据报套接字

使⽤传输层TCP协议

使用传输层UDP协议

有连接

无连接

可靠传输不可靠传输
面向字节流面向数据报
有接收缓冲区,也有发送缓冲区

有接收缓冲区,无发送缓冲区

大小不限大小受限:一次最多传输64k

    1.3 套接字通信模型     


    1.3.1 Java数据报套接字通信模型    


  • 对于 UDP 协议来说,具有无连接,面向数据报的特征,即每次都是没有建立连接,并且一次发送全部数据报,一次接收全部的数据报。

  • Java中使用 UDP 协议通信,主要基于 DatagramSocket 类来创建数据报套接字,并使用DatagramPacket 作为发送或接收的 UDP 数据报。

     对于一次发送及接收UDP数据报的流程如下    

以上只是一次发送端的UDP数据报发送,及接收端的数据报接收,并没有返回的数据。也就是只有请求,没有响应。


    对于一个服务端来说,重要的是提供多个客户端的请求处理及响应,流程如下    


    1.3.2 Java流套接字通信模型     



    1.4 Socket编程注意事项     


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

此时需要检查进程B绑定的是哪个端口,再查看该端口被哪个进程占用。以下为通过端口号查进程的方式:

  • 在cmd输入netstat -ano | findstr 端口号,则可以显示对应进程的pid。如以下命令显示了8888进程的pid


  • 在任务管理器中,通过pid查找进程


    解决端口被占用的问题:    


  • 如果占用端口的进程A不需要运行,就可以关闭A后,再启动需要绑定该端口的进程B;
  • 如果需要运行A进程,则可以修改进程B的绑定端口,换为其他没有使用的端口。

     2. UDP数据报套接字编程    


    2.1 API 介绍    


    2.1.1  DatagramSocket    


    概念    


计算机中的文件,通常是一个广义的概念,文件IO特指的是硬盘上的文件,是狭义的文件,除此之外,文件还可以代指一些硬件设备;


Socket 在计算机编程中,也可以认为是一种特殊的文件,打开 Socket 文件,也会在文件描述表中分配一个表项,来表示这个文件;

这样的文件特指网卡这样的硬件设备

对于网卡这样的硬件设备,在操作系统中就被抽象成 Socket 文件;这样的设定,主要是为了方便操作网卡;


直接操作网卡,需要往网卡的寄存器上写一些特定的数据,不好操作;操作系统管理一些硬件设备,是抽象成文件统一管理的;把操作网卡,转化成操作Socket文件,此时 Socket 文件,就相当于网卡的 “遥控器" ;


所以 DategramSocket ,就是一个用来表示网卡的文件,通过 DategramSocket 来操作网卡,只是加了一个 Dategram 前缀,意思就是基于UDP协议进行网络通信

DatagramSocket 是UDP Socket,用于发送和接收UDP数据报。 


    构造方法    


方法签名
 
方法说明
 
  DatagramSocket()  
 
创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
 
  DatagramSocket(int port)  
 
创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)

    方法     


方法签名
 
方法说明
 
void receive(DatagramPacket p)
 

读操作,从此套接字接收数据报

(如果没有接收到数据报,该方法会阻塞等待)

void send(DatagramPacket p)
 

写操作,从此套接字发送数据报包

(不会阻塞等待,直接发送)

void close()
 
关闭此数据报套接字

DatagramSocket 类的 receive() & sand() 的参数类型,都是 DatagramPacket ;

一个UDP数据包,就通过 DatagramPacket 对象来进行体现的,在进行receive() 或者 send(),都是按照 DatagramPacket 这样的数据包为定位进行接收的;


    2.1.2 DatagramPacket    


    定义    


DatagramPacket 是UDP Socket发送和接收的数据报。 


    构造方法    


 UDP 数据包的载荷数据,可以通过构造方法来指定

方法签名
 
方法说明
 
  DatagramPacket(byte[] buf, int length)  
 
  • 构造一个 DatagramPacket 用来接收数据报;
  • 接收的数据保存在字节数组(参数buf)中;
  • 接收的指定长度(第二个参数length);

  DatagramPacket  

  (byte[] buf, int offset,int  length,       SocketAddress address)  

  • 构造一个 DatagramPacket 用来发送数据报;
  • 发送的数据为字节数组(参数buf)中;
  • 从0到指定长度(第二个参数length);
  • address指定目的主机的IP和端口号

  DatagramPacket  

  (byte[] buf, int offset,int  length, 

 InetAddress address, int port)  

  • 构造一个 DatagramPacket 用来发送数据报;
  • 发送的数据为字节数组(参数buf)中;
  • 从0到指定长度(第二个参数length);
  • address,port 分别指定目的主机的IP和端口号

    方法    


方法签名
 
方法说明
 
 InetAddress getAddress() 
 
从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址
 
 int getPort() 
 
从接收的数据报中,获取发送端主机的端口号;或从发送的数据报中,获取接收端主机端口号
 
 byte[] getData() 
 
获取数据报中的数据

构造UDP发送的数据报时,需要传入SocketAddress,该对象可以使用InetSocketAddress 来创建; 


    2.1.3  InetSocketAddress ( SocketAddress的子类 )     


    构造方法    


方法签名
 
方法说明
 
  InetSocketAddress(lnetAddress addr,int port)  
 
创建一个Socket地址,包含IP地址和端口号

    2.2 实现回显服务器      


     2.2.1  实现原理    


  • 客户端给服务器发一个数据的操作,称为请求;
  • 服务器返回一个数据的操作,称为响应

  • 一个真实的服务器,请求和响应一般是不一样的,但是为了展示上述 API 的用法,就先不去管服务器中其他复杂的逻辑,写一个最简单的回显服务器(请求是什么,响应就是什么)

     2.2.2  代码实现    


    UDP Echo Server    

    (1) 构造一个 socket 对象代表网卡文件    


  • 输出 socket 文件内容,等于从网卡中读取数据;
  • 输入 socket 文件内容,等于向网卡内发送数据;


    (2) 实现启动服务器的 start()     


对于服务器来说,客户端啥时候发请求,发多少个请求,我们无法预测;
因此服务器中通常都需要有一个死循环,持续不断的尝试读取客户端的请求数据~~ 

在主循环中,需要实现的逻辑:

  1. 读取请求并解析;
  2. 根据请求,计算响应。(服务器最关键的逻辑),但是此处写的是回显服务器。这个环节相当于省略了;
  3. 把响应返回给客户端;

循环每执行一次,就相当于处理了一次请求,处理请求的过程,典型的服务器也是上面这三个步骤


    (3) 读取请求    


  • 服务器调用 receive() 对客户端发送的请求进行读取,等 receive() 方法执行完毕,参数里面对象的数据就是读取的结果:


      创建一个 DatagramPacket 对象,用于接收请求     


  • 创建 DatagramSocket 对象的时候,需要指定一个字节数组,并且传入接收的指定长度:

  • DatagramPacket 表示个UDP数据报。此处传入的字节数组,就保存 UDP的载荷部分;

     读取网卡中的数据包     


  • 把 DatagramPacket 对象(对象为全0)传给 receive();
     
  •  receive() 就会在方法内部把从响应数据报中读到的数据,填充到 requestPacket 这个引用指向的对象中;
  • receive() 执行完毕,参数里面的对象数据,就是需要从网卡中读取的数据(请求)

  • 这个过程就相当于我们在食堂打饭,把空的盘子(requestPacket )交给打饭阿姨( receive() ),阿姨会把打好饭(读到的数据)的盘子还给我们;

     (4) 解析请求    


  • 当前 UDP 载荷,是 DatagramSocket 对象的字节数组,存放着读取到的数据;
  • 这些读取到的数据是二进制数据,为了方便后续处理,我们把读取到的二进制数据转换成字符串形式;


  • 通过字节数组构造一个 String 对象,是构造 String 对象的一个典型做法;


  • 上述操作,表示拿到了一个数据报中的字节数组,把整个字节数组传给String对象,并且指定字节数组有效部分的范围,调用相应的构造方法,构成一个字符串;


    (5) 根据请求计算响应     


  • 这是服务器最关键的逻辑,但是此处写的是回显服务器,这个环节就相当于省略了;

  • 根据解析数据报,得到的请求 request,计算出响应 response 的操作,封装成一个方法,可以起到解耦合的作用;
  • 后续要写别的服务器,只需要修改 process() 的内容即可 ;

    (6) 把计算好的响应返回给客户端    


  • 如何根据响应 response 构造 DatagramSocket 对象呢?


  • 首先,需要拿出对响应数据报进行解析操作时,创建的字符串 request 里面的字节数组:


  • 要传入字节数组的长度,而不是使用字符串的长度,因为字符串的单位是字符,而我们要使用字节的个数,来作为当前 responsePacket 数据包的参数


   (6) 指定目的IP&目的端口     


  • 发送的响应数据报 responsePacket 是没有明确标注有发送的目的IP&目的端口的,要想正确地返回响应,就必须给响应数据报显式地标注目的IP & 目的端口;
  •  对于服务器返回响应的目的IP&目的端口,就是接到客户端请求的源IP&源端口:


  •  所以,通过调用 DatagramSocket 类中的 getSocketAddress(),该方法返回一个 InerSocketAddress 对象;


  • 这个对象包含了目标IP&目标端口号都在报头中,而不是不是在载荷中
  • 将这个对象作为参数,传给DatagramPacket对象,调用对应的构造方法;


  • 所以,将 getScketAddress() 方法返回的对象,作为参数来调用对应的构造方法,实例出的 responsePacket,就会显式地标注响应数据报的目标IP&目标端口:


     (7) 发送响应给客户端       

  • 服务器需要调用 send() 方法,把创建好的响应数据报作为响应,返回给客户端:

  •  send() 的构造方法

  •  所以,我们把刚刚构建好的响应数据报返回给客户端:

    (8) 打印日志来记录客户端/服务器交互的过程    



     (9) 判断当前 socket 对象(文件)是否需要关闭     


  • 文件是否需要关闭,考虑的是这个文件对象的生命周期是怎样的,此处的 socket 对象会自始至终伴随整个UDP服务器;
  • 只要服务器运行,就随时可能会从客户端中读数据,如果提前关闭 socket对象,那么UDP服务器继续运行也没有意义,所以socket对象,不能在服务器运行的过程中关闭;
  • 服务器关闭(进程结束),也不需要手动调用close(),因为进程结束时就会自动释放 PCB 的文件描述符表中的所有资源,
  • 所以当前socket文件不手动调用 close(),也是完全没问题的,因为socket的生命周期本来就需要跟随整个进程的;
  • 如果是有请求级别的文件对象,给一个请求,创建一个对象,就需要确保处理完毕之前,关闭对象。
  • 所以需要结合实际情况来确认一个对象的生命周期,通过生命周期,来决定对象是否应该关闭;

    (10) 补充    


  • DatagramPacket这个类说是一个UDP数据报,其实也包含了一些源信息;
  • 这个类有接收IP&端口号的属性,在通过 receive() 填充好DatagramPacket对象后,可以直接从对象中取出 UDP 数据包的来源(源IP和源端口)

  • 所以在将DatagramPacket这个UDP数据包,作为 receive() 方法的输入型参数时,不只是把UDP中的数据读进去了,还把IP&端口号等信息也读进去了;

  • 拿到请求数据包的源IP和源端口,就可以作为参数传给DatagramPacket对象,调用相应的构造方法;
  • UDP协议虽然没有保存发送请求数据包的地址信息,但是通过调用的构造方法,根据获取到的源IP和源端口,可以显式地指定响应数据包要发送的目的IP和目的端口 ;

    UDP Echo Client    

在客户端构造方法中传入的参数,就不能只有一个端口号了,要指定的是,当前客户端要连哪一个服务器,所以构造方法传要访问的服务器IP和端口号


UDP.本身不保存对端的信息,所以我们在代码中保存一下 



对比服务器调用DatagramSocket的构造方法,服务器调用的是不传端口号的构造方法:

因为客户端访问服务器,访问的目的 IP&目的端口,都是都是服务器的源IP&源端口;
服务器返回响应给客户端,目的IP 是客户端所在的主机 IP;目的端口,则随机使用一个操作系统分配的空闲端口;


   为什么客户端不推荐使用固定端口,而服务器推荐呢?  

     以餐饮店的老板和顾客交互的情景为例子:   


     对于老板(Server)    


  • 老板在选好一个地方后,就需要派发传单来宣传店铺,传单上有着餐饮店详细的地址(服务器源IP和源端口号)和菜谱(响应);
  • 对于这个餐饮店的地址(服务器IP&端口号),是必须要指定一个地方的(调用指定端口号的 DatagramSocket 的构造方法);
  • 如果餐厅地址不固定,就会影响顾客正常来店铺吃饭(客户端对服务器发出请求);


     对于顾客(Client)   


  • 当顾客光顾餐饮店的时候(客户端对服务器发出请求),会对老板点菜(服务器对客户端发送的请求作出响应);
  • 点菜后,需要找一个位置(服务器作出响应的目标IP&端口号)坐下,等待老板做菜(解析请求,计算响应);
  • 顾客在等待的过程中,会找一个空闲的位置坐下,这个位置是随机的,而不是指定的;(调用不指定端口号的构造方法,服务器返回响应时,随机使用一个操作系统分配的空闲端口;
  • 如果顾客要指定一个位置等待,就可能需要等待这个位置的另一位顾客用餐(不同应用程序竞争同一个端口号)


     总结:    


  • 为了能更好地接收客户端请求,需要指定服务器的IP和端口号;
  • 对于服务器,端口号是区分同一个主机不同的应用程序的同一时刻,不能有两个程序使用同一个端口(操作系统中,一个程序尝试关联一个非空闲的端口,就会关联失败)

实现 start() 方法


从控制台读取用户输入的内容


把请求发送给服务器,需要构造DatagramPacket对象,构造过程中,不光要构造载荷,还要设置服务器的IP和端口号 

需要把serverIp转换一下,通过InetAddress类提供的静态方法getByName,通过这样的方法,把字符串中的serverIp,转换成InetAddress对象


发送数据报请求给服务器



接收服务器响应


从服务器读取的数据( respose 中的二进制内容)进行读取解析,将读取到的数据转换成一个字符串,并且打印出来该字符串;


嵌套循环并设置循环结束条件


    Server & client 的交互过程    


    补充    


  • 127.0.0.1 是一个特殊的IP,是一个环回IP,表示当前主机;无论主机的 IP真实是什么,都可以使用127.0.0.1代替(类似于this);
  • 由于此时,客户端和服务器在同一个主机上,就可以使用127.0.0.1来访问,如果是不同主机就需要填写其他的IP了.

同时启动这两个程序的执行结果:


    UDP Dict Server    

编写⼀个英译汉的服务器,只需要继承 UdpEchoServer ,重写 process() 即可;



    字典服务器和客户端交互过程   


    同时启动进程,查看程序执行结果    


 这个报错说明当前端口被占用了,换一个端口即可;


    2.2.3  完整代码    


    UDP Echo Server     

package network;import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class UdpEchoServer {private DatagramSocket socket ;public UdpEchoServer(int port) throws SocketException {socket = new DatagramSocket(port);}public void start() throws IOException {System.out.println("启动服务器");while (true){DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);socket.receive(requestPacket);String request = new String(requestPacket.getData(),0,requestPacket.getLength());String response = process(request);DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,  requestPacket.getSocketAddress() ) ;socket.send(responsePacket);System.out.printf("[%s:%d] rep: %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(9092);server.start();}
}

    UDP Echo Client     

package network;import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {private DatagramSocket socket = null;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){System.out.println("请输入要发送的内容");if(!scanner.hasNext()){break;}String request = scanner.nextLine();DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length,InetAddress.getByName(serverIp) , serverPort);socket.send(requestPacket);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", 9092);client.start();}
}

    UDP Dict Server     

package network;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("小鸭","rabbit");dict.put("小雷","handsome guy");}@Overridepublic String process(String request){//查字典return dict.getOrDefault(request,"未找到该词条");}public static void main(String[] args) throws IOException {UdpDictServer server = new UdpDictServer(9092);server.start();}
}

        c96f743646e841f8bb30b2d242197f2f.gif

692a78aa0ec843629a817408c97a8b84.gif

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MacOS 如何连接 Linux NFS 服务器

以 Ubuntu 为例。 Ubuntu 服务器端设置 1. 进入 root 权限&#xff0c;安装 NFS 服务&#xff1a; apt-get update apt-get install nfs-kernel-server2. 创建共享目录&#xff1a; mkdir /data chown nobody:nogroup /data chmod 777 /data3. 配置 /etc/exports 文件: vi …

23种设计模式-原型(Prototype)设计模式

文章目录 一.什么是原型设计模式&#xff1f;二.原型模式的特点三.原型模式的结构四.原型模式的优缺点五.原型模式的 C 实现六.原型模式的 Java 实现七. 代码解析八.总结 类图&#xff1a; 原型设计模式类图 一.什么是原型设计模式&#xff1f; 原型模式&#xff08;Prototype…

Docker Buildx 与 CNB 多平台构建实践

一、Docker Buildx 功能介绍 docker buildx 是 Docker 提供的一个增强版构建工具&#xff0c;支持更强大的构建功能&#xff0c;特别是在构建多平台镜像和高效处理复杂 Docker 镜像方面。 1.1 主要功能 多平台构建支持 使用 docker buildx&#xff0c;可以在单台设备上构建…

C# 数据类型详解:掌握数据类型及操作为高效编码奠定基础

本文将带你深入了解C#中各种数据类型的特点、用途和最佳实践&#xff0c;让你不仅能熟练运用基本类型&#xff0c;还能掌握如何在实际项目中做出最合适的选择。 目录 C#基本语法 C#数据类型 C#类型转换 C#变量常量 C#基本语法 在学习C#之前我们要先知道C#的基础构建是由哪些…

新型大语言模型的预训练与后训练范式,谷歌的Gemma 2语言模型

前言&#xff1a;大型语言模型&#xff08;LLMs&#xff09;的发展历程可以说是非常长&#xff0c;从早期的GPT模型一路走到了今天这些复杂的、公开权重的大型语言模型。最初&#xff0c;LLM的训练过程只关注预训练&#xff0c;但后来逐步扩展到了包括预训练和后训练在内的完整…

Istio笔记01--快速体验Istio

Istio笔记01--快速体验Istio 介绍部署与测试部署k8s安装istio测试istio 注意事项说明 介绍 Istio是当前最热门的服务网格产品&#xff0c;已经被广泛应用于各个云厂商和IT互联网公司。企业可以基于Istio轻松构建服务网格&#xff0c;在接入过程中应用代码无需更改&#xff0c;…

uniapp运行时,同步资源失败,未得到同步资源的授权,请停止运行后重新运行,并注意手机上的授权提示。

遇到自定义基座调试时安装无效或无反应&#xff1f;本文教你用 ADB 工具快速解决&#xff1a;打开 USB 调试&#xff0c;连接设备&#xff0c;找到应用包名&#xff0c;一键卸载问题包&#xff0c;清理干净后重新运行调试基座&#xff0c;轻松搞定&#xff01; 问题场景&#…

CAD 文件 批量转为PDF或批量打印

CAD 文件 批量转为PDF或批量打印&#xff0c;还是比较稳定的 1.需要本地安装CAD软件 2.通过 Everything 搜索工具搜索&#xff0c;DWG To PDF.pc3 &#xff0c;获取到文件目录 &#xff0c;替换到代码中&#xff0c; originalValue ACADPref.PrinterConfigPath \ r"C:…

蓝桥杯每日真题 - 第23天

题目&#xff1a;&#xff08;直线&#xff09; 题目描述&#xff08;12届 C&C B组C题&#xff09; 解题思路&#xff1a; 题目理解: 在平面直角坐标系中&#xff0c;从给定的点集中确定唯一的直线。 两点确定一条直线&#xff0c;判断两条直线是否相同&#xff0c;可通过…

centos8:Could not resolve host: mirrorlist.centos.org

【1】错误消息&#xff1a; [rootcentos211 redis-7.0.15]# yum update CentOS Stream 8 - AppStream …

Android笔记(三十四):封装带省略号图标结尾的TextView

背景 项目需求需要实现在文本末尾显示一个icon&#xff0c;如果文本很长时则在省略号后面显示icon&#xff0c;使用TextView自带的drawableEnd可以实现&#xff0c;但是如果文本换行了则会显示在TextView垂直居中的位置&#xff0c;不满足要求&#xff0c;于是有了本篇的自定义…

CEF127 编译指南 Linux篇 - 安装Git和Python(三)

1. 引言 在前面的文章中&#xff0c;我们已经完成了基础开发工具的安装和配置。接下来&#xff0c;我们需要安装两个同样重要的工具&#xff1a;Git 和 Python。这两个工具在 CEF 的编译过程中扮演着关键角色。Git 负责管理和获取源代码&#xff0c;而 Python 则用于运行各种编…

centos系统设置本地yum源教程

在CentOS系统中,将ISO文件设置为本地源可以加快软件安装速度,特别是在没有网络连接的环境下。以下是详细步骤: 1. 下载和准备ISO镜像文件 首先,从CentOS的官方网站下载适合需求的CentOS ISO镜像文件。可以选择不同的版本,如CentOS 7或CentOS 8,以及适合你硬件架构的版本…

PDF view | Chrome PDF Viewer |Chromium PDF Viewer等指纹修改

1、打开https://www.browserscan.net/zh/ 2、将internal-pdf-viewer改为 internal-pdf-viewer-jdtest看下效果&#xff1a; 3、源码修改&#xff1a; third_party\blink\renderer\modules\plugins\dom_plugin_array.cc namespace { DOMPlugin* MakeFakePlugin(String plugin_…

模糊认知图模型、特征与推理

1. 基础知识 1.1认知图的发展 1948年&#xff0c;Tolman首次提到认知图&#xff3b;I]它把认知图描述为有向图&#xff0c;认为认知图是由一些弧连接起来的结点的集合&#xff0c;其目的是为心理学构建一个模型。后来&#xff0c;认知图被其他学者所借用&#xff0c;不同的学…

Mac 环境下类Xshell 的客户端介绍

在 Mac 环境下&#xff0c;类似于 Windows 环境中 Xshell 用于访问 Linux 服务器的工具主要有以下几种&#xff1a; SecureCRT&#xff1a; 官网地址&#xff1a;https://www.vandyke.com/products/securecrt/介绍&#xff1a;支持多种协议&#xff0c;如 SSH1、SSH2、Telnet 等…

玩转 uni-app 静态资源 static 目录的条件编译

一. 前言 老生常谈&#xff0c;了解 uni-app 的开发都知道&#xff0c;uni-app 可以同时支持编译到多个平台&#xff0c;如小程序、H5、移动端 App 等。它的多端编译能力是 uni-app 的一大特点&#xff0c;让开发者可以使用同一套代码基于 Vue.js 的语法编写程序&#xff0c;然…

【西瓜书】支持向量机(SVM)

支持向量机&#xff08;Support Vector Machine&#xff0c;简称SVM&#xff09;。 超平面 分类学习最基本的想法就是基于训练集合D在样本空间中找到一个划分超平面&#xff0c;将不同类别的样本分开。 但能将训练样本分开的划分超平面可能有很多&#xff0c;应该努力去找到哪…

【开源免费】基于SpringBoot+Vue.JS宠物咖啡馆平台(JAVA毕业设计)

博主说明&#xff1a;本文项目编号 T 064 &#xff0c;文末自助获取源码 \color{red}{T064&#xff0c;文末自助获取源码} T064&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析…

海康VsionMaster学习笔记(学习工具+思路)

一、前言 VisionMaster算法平台集成机器视觉多种算法组件&#xff0c;适用多种应用场景&#xff0c;可快速组合算法&#xff0c;实现对工件或被测物的查找测量与缺陷检测等。VM算法平台依托海康威视在图像领域多年的技术积淀&#xff0c;自带强大的视觉分析工具库&#xff0c;可…