【计算机网络】网络编程

news2025/3/25 20:12:05

文章目录

  • 1. 客户端/服务器
  • 2. TCP/UDP协议
  • 3. 网络编程套接字-socket
    • 3.1 API的使用
    • 3.1 DatagramScoket类
    • 3.1 DatagramScoket类
  • 4. 通过UDP实现回显服务器程序
    • 4.1 服务器代码
    • 4.2 客户端代码
    • 4.3 代码执行过程
    • 4.4 通过UDP实现翻译客户端
  • 5. 通过TCP实现回显服务器
    • 5.1 服务器代码
    • 5.2 客户端代码
    • 5.3 代码执行过程

1. 客户端/服务器

在网络中,主动发起请求的一方就是客户端,被动接受的一方就是服务器。
客户端发送给服务器的数据,叫做请求(request)。
服务器返回给客户端的数据,叫做响应(response)。

客户端-服务器之间的交互:

一问一答<----->场景:web开发
一问多答<----->场景:下载
多问一答<----->场景:上传
多问多答<----->场景:远程控制

2. TCP/UDP协议

进行网络通信,需要调用系统的api,本质上是传输层提供的传输层,涉及到的协议主要是TCP和UDP。
区别:

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

连接:网络上的连接是抽象的,本质上就是通信双方保存了对方的相关信息。

有连接类似于打电话,需要对方接通
无连接类似于发短信,无需对方接通

可靠传输:这里的可靠传输就是发的数据到没到,发送方能够清楚的感知到。

面向字节流:网络中传输的数据的基本单位是字节。
面向数据报:每次传输的基本单位是数据报。

全双工:一个信道可以双向通信,就像公路一样是双向车道。
半双工:只能单向通信,就像过独木桥。

3. 网络编程套接字-socket

socket 是操作系统给应用程序(传输层给应用层)提供的API,Java对这个API进行了封装。
socket提供了两组不同的 API,UDP有一套,TCP有一套,本文主要介绍api的使用

3.1 API的使用

Java把系统原生的API进行了封装,操作系统中有一类文件叫做 scoket 文件,抽象的表示了"网卡"这样的设备,通过操作scoket文件就可以对网卡进行操作。

通过网卡发送数据,就是写scoket文件
通过网卡接收数据,就是读socket文件

3.1 DatagramScoket类

DatagramScoket是UDP scoket,用于接收和发送数据报

构造方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(port就是端口号)
内置方法说明(下面的DatagramPacket p是作为输出型参数的)
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报包(不会阻塞等待,直接发送)
void close()关闭数据报套接字

输出型参数:输出型参数是一个变量,函数会修改它的值,并将修改后的值传递回调用者。调用者可以通过这个参数获取函数处理后的数据。就像是我们自己带饭盒去食堂吃饭,饭盒就相当于DatagramPacket,打饭的阿姨会帮我们把饭盒装满饭菜,此时打饭的阿姨就是void receive。

3.1 DatagramScoket类

UDP面向数据报,每次发送接收数据的基本单位,就是一个UDP数据报。

构造方法说明
DatagramPacket(byte[] buf, int length)构造一个 DatagramPacket 用来接收数据报,接收的数据保存在字节数组 buf 中,接收指定的长度 length
DatagramPacket(byte[] buf,int offset,int length,SocketAddress address)构造一个 DatagramPacket 用来接收数据报,接收的数据保存在字节数组 buf 中,接收指定的长度 length,address 表示指定的目的主机的 ip 和端口号
内置方法说明(下面的DatagramPacket p是作为输出型参数的)
InetAddress getAddress()从接收的数据报中,获取发送端主机 IP 地址;或从发送的数据报中,获取接收端主机IP地址
int getPort()从接收的数据报中,获取发送端主机的端口号;或者从发送的数据报中,获取接收端主机的端口号
byte[] getData()获取数据报中的数据

4. 通过UDP实现回显服务器程序

回显服务器(Echo Server)是一种网络服务器,其主要功能是将接收到的数据原样返回给发送者。

4.1 服务器代码

  • 创建 DatagramSocket 对象,接下来操作网卡,操作网卡都是通过 socket 对象来完成的,此时创建 DatagramSocket 对象需要指定端口号,以方便客户端寻找服务器。
  • 对于服务器来说,需要不断地接受请求与返回响应,所以这里一直while循环,直至被强制终止。
  • receive从网卡能读取到一个 UDP 数据报,并放到了 requestPacket 对象中。
    其中 UDP 数据报的载荷部分就被放到 requestPacket 内置的字节数组中。
    另外报头部分,也会被 requestPacket 的其他属性保存,除了 UDP 报头之外,还有其他信息,比如收到的数据源 IP…
    通过 requestPacket 获取数据报的源 ip、源端口
  • 传输层会为每个socket对象分配一个缓冲区(内核里),此处给socket分配的缓冲区就是“阻塞队列”。
  • 因为这个是回显服务器,所以process没有任何操作。
package network;

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 requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);
            //读到的字节数组,转成String方便后续的逻辑处理
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            //2.根据请求计算相应(对于辉县服务器来说,这一步啥都不用做)
            String response = process(request);
            //3.把响应返回给客户端
            //构造一个DatagramPacket作为响应对象
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length, requestPacket.getSocketAddress());
            socket.send(responsePacket);

            //打印日志
            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();
    }
}

4.2 客户端代码

  • 客户端不需要手动指定端口,因为不知道用户客户端哪个端口空闲。
  • 客户端给服务器发送请求时,需要知道服务器的IP地址与端口号。
  • 使用 scanner 读取字符串,最好使用 next 而不是 nextLine。
    如果使用 nextLine 读取,需要手动输入换行符,enter 来进行控制,由于 enter 键不仅仅会产生 \n 还会产生其他字符,就会导致读取到的内容就容易出问题。
    使用 next 其实是以"空白符" 作为分隔符,包括不限于换行, 回车,空格, 制表符,垂直制表符…
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;//这里是十进制位的IP地址
        socket = new DatagramSocket();
    }

    public void start() throws IOException {
        System.out.println("客户端启动");
        Scanner scanner = new Scanner(System.in);
        while (true) {
            //1.从控制台上读取要发送的数据
            System.out.print("->");//输入
            if (!scanner.hasNext()){
                break;
            }
            String request = scanner.next();
            //2.构造请求并发送
            DatagramPacket requestPacker = new DatagramPacket(request.getBytes(),request.getBytes().length,
                    InetAddress.getByName(serverIp),serverPort);//这里是改为二进制后的IP地址
            socket.send(requestPacker);
            //3.读取服务器的响应
            DatagramPacket responsePacker = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacker);
            //4.把响应显示在控制台
            String response = new String(responsePacker.getData(),0,responsePacker.getLength());
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient udpEchoClient = new UdpEchoClient("127.0.0.1", 9090);
        udpEchoClient.start();
    }
}

4.3 代码执行过程

  1. 服务器启动,启动之后,立刻进入while循环,执行到receive,进入阻塞。此时没有任何客户端发来请求。

  2. 客户端启动,启动之后,立刻进入while循环,执行到hasNext,进入阻塞。此时用户没有在控制台输入任何内容。

  3. 用户在客户端的控制台中输入字符串,按下回车,此时hasNext阻塞解除,next会返回刚才输入的内容。
    基于用户输入的内容,构造出一个DatagramPacket对象,并进行send。
    send执行完毕之后,执行到receive操作,等待服务器返回的响应数据。

  4. 服务器收到请求之后,就会从receive的阻塞中返回。
    返回之后,就会根据读到的DatagramPacket对象,构造String request,通过process方法构造一个String response。
    再根据response构造一个DatagramPacket表示响应对象,在通过send来进行发送给客户端。
    执行这个过程中,客户端始终在阻塞等待。

  5. 客户端从receive中进行返回,就能够得到服务器返回的响应,并且打印在控制台上。
    与此同时,服务器也进入下一次循环,也要进入到第二次的receive阻塞,等待下一个请求。

4.4 通过UDP实现翻译客户端

package network;

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;

public class UdpDictServer extends UdpEchoServer{
    private HashMap<String,String> hashMap = new HashMap<>();

    public UdpDictServer(int port) throws SocketException {
        super(port);

        hashMap.put("cat","小猫");
        hashMap.put("dog","小狗");
        hashMap.put("chicken","小鸡");
    }

    //start() 方法完全从父类集成下来即可
    //process() 方法要进行重写,加入咱们自己的业务逻辑,进行翻译


    @Override
    public String process(String request) {
        return hashMap.getOrDefault(request,"您查的单词不存在");
    }

    public static void main(String[] args) throws IOException {
        UdpDictServer udpDictServer = new UdpDictServer(9090);
        udpDictServer.start();
    }
}

5. 通过TCP实现回显服务器

TCP是面向字节流的,传输的基本单位是字节,TCP协议是需要建立连接的。

连接建立:从客户端Socket的构造方法发送连接请求,服务器的SerevrSocket监听到请求后并且调用accept()方法,这样就建立了连接,然后accept()方法在服务器中会生成一个新的Socket对象用来进行通信。

构造方法说明
ServerSocket(int port)创建⼀个服务端流套接字Socket,并绑定到指定端⼝
Socket(String host, int port)创建⼀个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接

ServerSocket 类的内置方法:

内置方法说明
Socket accept()开始监听指定端⼝(创建时绑定的端⼝),有客户端连接后,返回一个Socket对象,并且基于Socket建立与客户端的连接,没有就阻塞等待
void close()关闭套接字

Socket 类的内置方法:

内置方法说明
InetAddress getInetAddress()返回套接字锁连接的地址
InputStream getInputStream()返回此套接字的输⼊流
InputStream getOutputStream()返回此套接字的输出流

ServerSocket只能在服务器中使用,而Socket既可以在服务器中使用也可以在客户端使用。
TCP是有连接的,就类似需要客户端拨打电话,服务器来接听。

5.1 服务器代码

  • 通过 ServerSocket 提供的构造方法,给服务器分配一个端口号(这里我们需要注意,服务器是必须要指定端口号的,而客户端系统自己分配端口号就可以)
  • 服务器的 ServerSocket 是用来监听请求的,如果有客户端发送请求,那么ServerSocket就会感知到。
  • 当 ServerSocket 监测到有客户端发来连接的请求,ServerSocket 会调用accept()方法,accept()方法会返回一个新的Socket对象,这就算是建立了连接而这个新的对象就是用来与客户端通信。
  • accept() 是一个可能会产生阻塞的操作,如果没有客户端连过来,会一直阻塞。
  • 在客户端运行完毕之后我们要close()。
    TCP的客户端只有一个,随着客户端越来越多,消耗的socket也会越来越多,如果不释放,可能会把文件描述符表占满。
    serverSocket 整个程序只有唯一一个对象,并且这个对象的生命周期很长是要跟随整个程序的,这个对象无法提前关闭。只要程序退出,随着进程的销毁一起被释放即可。(不需要手动进行)

过程就像是客户端的 Socket 想要通过服务器的 ServerSocket 认识服务器中的 Socket。于是客户端的 Socket 就请求服务器的 ServerSocket 帮忙牵线搭桥,服务器的 ServerSocket 就把服务器的 Socket 的电话号码给了客户端,而客户端的构造方法就类似于给服务器拨通了电话,而当前只是在响铃,而accept()方法就类似接听,只有调用accept()的方法后才算真正建立连接。

  • 陌生代码讲解
InputStream inputStream=clientSocket.getInputStream();
OutputStream outputStream=clientSocket.getOutputStream();

从网卡内读数据以及往网卡内写数据,TCP中操作socket文件,对其进行读写(InputStream,OutputStream),就是在操作网卡,操作系统把网卡抽象成了一个文件。

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 TcpEchoSever {
    private ServerSocket serverSocket = null;

    public TcpEchoSever(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动");
        while (true) {
            //通过accept方法来“接听电话”,然后才能通信.如果没有客户端连过来,会进入阻塞状态
            Socket clientSocket = serverSocket.accept();
            processConnention(clientSocket);
        }
    }

    //通过这个方法来处理一次连接,连接建立的过程中涉及到多次的请求响应交互
    private void processConnention(Socket clientSocket) {
        System.out.printf("[%s: %d] 客户端上线\n", clientSocket.getInetAddress(), clientSocket.getPort());
        //循环读取客户端的请求并返回响应
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream();) {
            while (true) {
                Scanner scanner = new Scanner(inputStream);
                if (!scanner.hasNext()) {
                    //读取完毕,客户端断开连接,
                    System.out.printf("[%s: %d] 客户端下线\n", clientSocket.getInetAddress(), clientSocket.getPort());
                    break;
                }
                //1.读取请求并解析,这里注意隐藏约定,next 读到空白符(\n 或者 空格)才结束
                String request = scanner.next();
                //2.根据请求计算响应
                String response = process(request);
                //3.把响应返回给客户端
                //下行代码可以写会,但是这种方式不方便给返回的响应中添加 \n
                // outputStream.write(response.getBytes(),0,response.getBytes().length);
                //可以给 outputStream 套上一层,完成更方便的写入
                PrintWriter printWriter = new PrintWriter(outputStream);
                printWriter.println(response);
                printWriter.flush();//通过主动刷新缓冲区,确保数据真正发送出去

                System.out.printf("[%s: %d] req: %s, resp: %s\n", clientSocket.getInetAddress(), clientSocket.getPort(), request, response);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                clientSocket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoSever tcpEchoSever = new TcpEchoSever(9090);
        tcpEchoSever.start();
    }
}

5.2 客户端代码

  • 陌生代码讲解
 Scanner scannerConsole = new Scanner(System.in);
 Scanner scannerNetwork = new Scanner(inputStream);

在客户端上,scannerConsle 是在控制台中读取数据,也就是我们用户输入的时候读取数据,并且转变为 String 发送给服务器(就是通过OutputStream写入操作网卡的文件)。scannerNetwork 就是在服务器做出响应后,通过 inputStream 读取网卡上的数据 最终打印出结果。

  PrintWriter writer = new PrintWriter(outputStream);
   //2.把请求发送给服务器,这里使用println来发送,是为了让末尾带有\n,与服务器的scanner.next呼应
  writer.println(request);
  writer.flush();//通过主动刷新缓冲区,确保数据真正发送出去

PrintWriter 是 Java 中的一个类,位于 java.io 包中,用于以文本形式写入输出数据。它继承了 Writer 抽象类,提供了多种方法来方便地写入字符和字符串到文件或其他输出流中。
flush() 方法的作用是刷新缓冲区。因为IO都是比较低效的操作,一次一次读写,太麻烦。缓冲区就将先把数据放到内存缓冲区中,等攒够了数据一起发送,这样就变得高效了,而flush() 就是将缓存区刷新,将数据一点一点发送出去,不用等到满了一股脑发出去。

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;

    public TcpEchoClient(String severIp, int severPort) throws IOException {
        //此处直接将ip和port传给socket对象。由于TCP是有连接的,因此socket里面会保存好这两信息,故TcpEchoClient就不用保存
        socket = new Socket(severIp,severPort);
    }

    public void start() {
        System.out.println("客户端启动");
        try(InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream()) {
            Scanner scannerConsole = new Scanner(System.in);
            Scanner scannerNetwork = new Scanner(inputStream);
            PrintWriter writer = new PrintWriter(outputStream);
            while(true){
                //1.从控制台读取输入的字符串
                System.out.print("->");
                if (!scannerConsole.hasNext()){
                    break;
                }
                String request = scannerConsole.next();
                //2.把请求发送给服务器,这里使用println来发送,是为了让末尾带有\n,与服务器的scanner.next呼应
                writer.println(request);
                writer.flush();//通过主动刷新缓冲区,确保数据真正发送出去
                //3.从服务器读取响应,与服务器返回响应的逻辑相呼应
                String response = scannerNetwork.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();
    }
}

5.3 代码执行过程

  1. 服务器启动,阻塞在accept(),等待客户端连接。
  2. 客户端启动,创建一个Socket对象,触发客户端与服务器之间的建立连接的操作,此时,服务器将从accpet的阻塞状态中返回。
  3. 从阻塞状态中返回后,进入到 processConnection 方法,这里执行到 hasNext() 产生阻塞,因为虽然建立连接,但是没有发来任何请求,hasNext() 阻塞等待请求到达。
  4. 客户端继续执行到 hasNext() ,等待用户向控制台写入内容。
  5. 用户在控制条输入内容,从 hasNext() 的阻塞返回,继续执行发送请求的逻辑,将请求发出去后,将会等待服务器的返回,此时也会由 next() 产生阻塞。
  6. 服务器从 hasNext() 的阻塞状态返回读取到请求内容并进行处理,构造出响应,将响应写会客户端,此时,服务器结束这次循环,开启下一轮循环,继续阻塞在 hasNext() 等待下一个请求。
  7. 客户端读取到响应,并显示出来,结束这次循环,继续阻塞在 hasNext() 等待用户的输入

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【蓝桥杯】4535勇闯魔堡(多源BFS + 二分)

思路 k有一个范围&#xff08;0到怪物攻击的最大值&#xff09;&#xff0c;求满足要求的k的最小值。很明显的二分套路。 关键是check函数怎么写&#xff0c;我们需要找到一条从第一行到最后一行的路径&#xff0c;每一次可以从上下左右四个方向前进&#xff0c;那么我么可以用…

HTML图像标签的详细介绍

1. 常用图像格式 格式特点适用场景JPEG有损压缩&#xff0c;文件小&#xff0c;不支持透明适合照片、复杂图像PNG无损压缩&#xff0c;支持透明&#xff08;Alpha通道&#xff09;适合图标、需要透明背景的图片GIF支持动画&#xff0c;最多256色简单动画、低色彩图标WebP谷歌开…

Chapter 4-15. Troubleshooting Congestion in Fibre Channel Fabrics

show zone member: Shows the name of the zone to which a device belongs to. This command can be used to find the victims of a culprit device or vice versa. 显示设备所属的区域名称。该命令可用于查找罪魁祸首设备的受害者,反之亦然。 show zone active: Shows the…

QT三 自定义控件

一 自定义控件 现在的需求是这样&#xff1a; 假设我们要在QWidget 上做定制&#xff0c;这个定制包括了关于 一些事件处理&#xff0c;意味着要重写QWidget的一些代码&#xff0c;这是不实际的&#xff0c;因此我们需要自己写一个MyWidget继承QWidget&#xff0c;然后再MyWi…

在 ASP .NET Core 9.0 中使用 Scalar 创建漂亮的 API 文档

示例代码&#xff1a;https://download.csdn.net/download/hefeng_aspnet/90407900 Scalar 是一款可帮助我们为 API 创建精美文档的工具。与感觉有些过时的默认 Swagger 文档不同&#xff0c;Scalar 为 API 文档提供了全新而现代的 UI。其简洁的设计让开发人员可以轻松找到测试…

用于 RGB-D 显著目标检测的点感知交互和 CNN 诱导的细化网络(问题)

摘要 问题一&#xff1a;但在对自模态和跨模态的全局长距离依赖关系进行建模方面仍显不足。什么意思&#xff1f; 自模态&#xff08;Intra-modal&#xff09;全局依赖&#xff1a;在同一模态内&#xff0c;长距离像素之间的信息交互对于理解全局背景很重要&#xff0c;但 CN…

python3 -m http.sever 8080加载不了解决办法

解决方法很多&#xff0c;本文设置各种处理方法&#xff0c;逻辑上需要根据你的自身情况选择 我会告诉你遇到这种问题怎么做&#xff0c;根据具体症状处理 如需转载&#xff0c;标记出处 背景&#xff1a; 1。如图 2.。域名访问不了 http://www.meiduo.site:8080/register.ht…

Oracle数据库性能优化全攻略:十大关键方向深度解析与实践指南

文章目录 一、SQL查询优化二、索引优化三、内存管理四、I/O优化五、分区表与分区索引六、并行处理七、统计信息管理八、锁与并发控制九、数据库参数调优十、应用设计优化结论 在当今数据驱动的时代&#xff0c;数据库的性能优化成为了确保企业应用高效运行的关键。Oracle作为业…

windows清除电脑开机密码,可保留原本的系统和资料,不重装系统

前言 很久的一台电脑没有使用了&#xff0c;开机密码忘了&#xff0c;进不去系统 方法 1.将一个闲置u盘设置成pe盘&#xff08;注意&#xff0c;这个操作会清空原来u盘的数据&#xff0c;需要在配置前将重要数据转移走&#xff0c;数据无价&#xff0c;别因为配置这个丢了重…

【深度学习】【目标检测】【Ultralytics-YOLO系列】Windows11下YOLOV3人脸检测

【深度学习】【目标检测】【Ultralytics-YOLO系列】Windows11下YOLOV3人脸检测 文章目录 【深度学习】【目标检测】【Ultralytics-YOLO系列】Windows11下YOLOV3人脸检测前言YOLOV3模型运行环境搭建YOLOV3模型运行数据集准备YOLOV3运行模型训练模型验证模型推理导出onnx模型 总结…

html5-qrcode前端打开摄像头扫描二维码功能

实现的效果如图所示&#xff0c;全屏打开并且扫描到二维码后弹窗提醒&#xff0c;主要就是使用html5-qrcode这个依赖库&#xff0c;html5-qrcode开源地址&#xff1a;GitHub - mebjas/html5-qrcode: A cross platform HTML5 QR code reader. See end to end implementation at:…

ui_auto_study(持续更新)

通过where python来找到python解释器的安装目录 如果不适配&#xff0c;谷歌浏览器插件可以在这个地址下载对应的驱动 谷歌浏览器驱动下载地址 下载对应的驱动版本&#xff0c;替换原驱动 替换后&#xff0c;可以执行成功 div代表标签 .开头的代表类# 使用class定位元素 …

【nodejs】爬虫路漫漫,关于nodejs的基操

一.下载安装nodejs 官网地址&#xff1a;Node.js — 在任何地方运行 JavaScript 二.下载安装vscode代码编辑器 官网地址&#xff1a;Download Visual Studio Code - Mac, Linux, Windows 三.修改本地脚本策略 1&#xff0c;windowsi 打开电脑设置 2&#xff0c;输入powersh…

常见中间件漏洞攻略-Jboss篇

一、CVE-2015-7501-Jboss JMXInvokerServlet 反序列化漏洞 第一步&#xff1a;开启靶场 第二步&#xff1a;访问该接口&#xff0c;发现直接下载&#xff0c;说明接⼝开放&#xff0c;此接⼝存在反序列化漏洞 http://47.103.81.25:8080/invoker/JMXInvokerServlet 第三步&…

quartz.net条件执行

quartz.net条件执行 在使用Quartz.NET时&#xff0c;你可能需要基于某些条件来决定是否执行一个任务。Quartz.NET本身并不直接支持基于条件执行任务的功能&#xff0c;但你可以通过一些策略来实现这一需求。下面是一些方法来实现基于条件的任务执行&#xff1a; 1. 使用触发器…

docker利用ollama +Open WebGUI在本地搭建部署一套Deepseek-r1模型

系统&#xff1a;没有限制&#xff0c;可以运行docker就行 磁盘空间&#xff1a;至少预留50GB; 内存&#xff1a;8GB docker版本&#xff1a;4.38.0 桌面版 下载ollama镜像 由于docker镜像地址&#xff0c;网络不太稳定&#xff0c;建议科学上网的一台服务器拉取ollama镜像&am…

ccfcsp3302相似度计算

//相似度计算 #include<iostream> #include<set>//不重复 #include<string> using namespace std; int main() {int n, m;cin >> n >> m;set<string>str1;set<string>str2;for(int i0;i<n;i){string s;cin>>s;for(int j0;…

DeepSeek和Kimi在Neo4j中的表现

以下是2个最近爆火的人工智能工具&#xff0c; DeepSeek:DeepSeek Kimi: Kimi - 会推理解析&#xff0c;能深度思考的AI助手 1、提示词&#xff1a; 你能帮我生成一个知识图谱吗&#xff0c;等一下我会给你一篇文章&#xff0c;帮我从内容中提取关键要素&#xff0c;然后以N…

【Java】TCP网络编程:从可靠传输到Socket实战

活动发起人小虚竹 想对你说&#xff1a; 这是一个以写作博客为目的的创作活动&#xff0c;旨在鼓励大学生博主们挖掘自己的创作潜能&#xff0c;展现自己的写作才华。如果你是一位热爱写作的、想要展现自己创作才华的小伙伴&#xff0c;那么&#xff0c;快来参加吧&#xff01…

剑指小米特斯拉:秦L EV上市11.98万起

3月23日&#xff0c;比亚迪王朝网推出全新中级纯电轿车秦L EV&#xff0c;价格区间为11.98万-13.98万元&#xff0c;瞬间火爆市场。 依托e平台3.0 Evo技术赋能&#xff0c;秦L EV以“国潮设计、智能座舱、越级空间、高效安全、高阶智驾”五大核心优势&#xff0c;直击年轻用户痛…