10.JAVAEE之网络编程

1.网络编程

  • 通过网络,让两个主机之间能够进行通信 =>基于这样的通信来完成一定的功能
  • 进行网络编程的时候,需要操作系统给咱们提供一组 AP1, 通过这些 API才能完成编程(API 可以认为是 应用层 和 传输层 之间交互的路径)(API:Socket API相当于一个插座:通过这一套 Socket AP| 可以完成不同主机之间,不同系统之间的网络通信)
  • 传输层,提供的网络协议,主要是两个:TCP UDP
  • 这俩协议的特性(工作原理) 差异很大.导致,使用这两种协议进行网络编程,也存在一定差别系统就分别提供了两套 API
  • TCP 和 UDP 的区别.(后面网络原理章节, 学习的重点)

       1.TCP 是有连接的, UDP 是无连接的

       2.TCP 是可靠传输的,UDP 是不可靠传输的

       3.TCP 是面向字节流的,UDP 是面向数据报

       4.TCP 和 UDP 都是全双工的

 1.TCP 是有连接的, UDP 是无连接的
(连接 是 抽象 的概念)
计算机中,这种 抽象 的连接是很常见的,此处的连接本质上就是建立连接的双方,各自保存对方的信息两台计算机建立连接,就是双方彼此保存了对方的关键信息~~
TCP 要想通信, 就需要先建立连接 (刚才说的, 保存对方信息),做完之后,才能后续通信(如果 A 想和 B 建立连接, 但是 B 拒绝了! 通信就无法完成!!!)

UDP 想要通信,就直接发送数据即可~~不需要征得对方的同意,UDP 自身也不会保存对方的信息(UDP 不知道,但是写程序的人得知道.UDP 自己不保存,但是你调用 UDP 的 socket api的时候要把对方的位置啥的给传过去)

2.TCP 是可靠传输的,UDP 是不可靠传输的
网络上进行通信, A ->B 发送一个消息,这个消息是不可能做到 100% 送达的!! 

可靠传输,退而求其次.
A ->B 发消息,消息是不是到达 B 这一方,A 自己能感知到.(A 心里有数)进一步的,就可以在发送失败的时候采取一定的措施(尝试重传之类的)

TCP 就内置了可靠传输机制;UDP 就没有内置可靠传输

【tips】可靠传输,听起来挺美好的呀, 为啥不让 UDP 也搞个可靠传输呢??

想要可靠传输,你就是要付出代价的(需要去交换)
可靠传输要付出什么代价?
1)机制更复杂
2)传输效率会降低

3.TCP 是面向字节流的,UDP 是面向数据报
此处说的 字节流 和 文件 操作这里的 字节流 是一个意思!!!
TCP 也是和文件操作一样,以字节为单位来进行传输.
UDP 则是按照数据报为单位,来进行传输的
UDP 数据报是有严格的格式的 

网络通信数据的基本单位,涉及到多种说法~~
1.数据报(Datagram)
2.数据包(Packet)

3.数据帧(Frame)
4.数据段 (Segment) 

4. TCP 和 UDP 都是全双工的
一个信道,允许双向通信, 就是全双工
一个信道,只能单向通信,就是半双工
代码中使用一个 Socket 对象, 就可以发送数据也能接受数据~~ 

2.UDP 的 socket api 如何使用

Datagramsocket 

Datagrampacket 

【回显服务器:(echo server)】

写一个简单的 UDP 的客户端/服务器 通信的程序.
这个程序没有啥业务逻辑,只是单纯的调用 socket api.
让客户端给服务器发送一个请求,请求就是一个从控制台输入的字符串.
服务器收到字符串之后,也就会把这个字符串原封不动的返回给客户端,客户端再显示出来. 

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoServer {
    // 创建一个 DatagramSocket 对象. 后续操作网卡的基础.
    private DatagramSocket socket = null;

    public UdpEchoServer(int port) throws SocketException {
        // 这么写就是手动指定端口
        socket = new DatagramSocket(port);
        // 这么写就是让系统自动分配端口
        // socket = new DatagramSocket();
    }

    public void start() throws IOException {
        // 通过这个方法来启动服务器.
        System.out.println("服务器启动!");
        // 一个服务器程序中, 经常能看到 while true 这样的代码.
        while (true) {
            // 1. 读取请求并解析.
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);
            // 当前完成 receive 之后, 数据是以 二进制 的形式存储到 DatagramPacket 中了.
            // 要想能够把这里的数据给显示出来, 还需要把这个二进制数据给转成字符串.
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            // 2. 根据请求计算响应(一般的服务器都会经历的过程)
            //    由于此处是回显服务器, 请求是啥样, 响应就是啥样.
            String response = process(request);
            // 3. 把响应写回到客户端.
            //    搞一个响应对象, DatagramPacket
            //    往 DatagramPacket 里构造刚才的数据, 再通过 send 返回.
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress());
            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();
    }
}
import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp = "";
    private int serverPort = 0;

    public UdpEchoClient(String ip, int port) throws SocketException {
        // 创建这个对象, 不能手动指定端口.
        socket = new DatagramSocket();
        // 由于 UDP 自身不会持有对端的信息. 就需要在应用程序里, 把对端的情况给记录下来.
        // 这里咱们主要记录对端的 ip 和 端口 .
        serverIp = ip;
        serverPort = port;
    }

    public void start() throws IOException {
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        while (true) {
            // 1. 从控制台读取数据, 作为请求
            System.out.print("-> ");
            String request = scanner.next();
            // 2. 把请求内容构造成 DatagramPacket 对象, 发给服务器.
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIp), serverPort);
            socket.send(requestPacket);
            // 3. 尝试读取服务器返回的响应了.
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            // 4. 把响应, 转换成字符串, 并显示出来.
            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);
        // UdpEchoClient client = new UdpEchoClient("42.192.83.143", 9090);
        client.start();
    }
}

【客户端】

  • 对于服务器来说,也就需要把端口号给明确下来了~~
  • 客户端的端口号是不需要确定的.交给系统进行分配即可,
  • 如果你手动指定确定的端口,就可能和别人的程序的端口号冲突
  • 【tips】服务器这边手动指定端口,就不会出现冲突嘛??
    为啥客户端在意这个冲突,而服务器不在意呢??

    服务器是在程序猿手里的,一个服务器上都有哪些程序, 都使用哪些端口,程序猿都是可控的!!程序猿写代码的时候,就可以指定一个空闲的端口,给当前的服务器使用即可
    但是客户端就不可控,客户端是在用户的电脑上;一方面,用户千千万~~ 每个用户电脑上装的程序都不一样,占用的端口也不一样;交给系统分配比较稳妥.系统能保证肯定分配一个空闲的端口

服务器一旦启动,就会立即执行到这里的 receive 方法此时,客户端的请求可能还没来呢~~
这种情况也没关系.receive 就会直接阻塞, 就会一直阻塞到真正客户端把请求发过来为止,(类似于阻塞队列)

【question】

//根据请求计算响应(核心步骤)

这个步骤是一个服务器程序,最核心的步骤!!!
咱们当前是 echo server 不涉及到这些流程,也不必考虑响应怎么计算,只要请求过来,就把请求当做响应

【question】

【question】上述写的代码中,为啥没写 close??
socket 也是文件,不关闭不就出问题了,不就文件资源泄露了么,为啥这里咱们可以不写 close?为啥不写 close 也不会出现文件资源泄露??

private DatagramSocket socket = null;
这个 socket 在整个程序运行过程中都是需要使用的(不能提前关闭)当 socket 不需要使用的时候, 意味着程序就要结束了

进程结束,此时随之文件描述符表就会销毁了(PCB 都销毁了).谈何泄露??
随着销毁的过程,被系统自动回收了~~

啥时候才会出现泄露?代码中频繁的打开文件,但是不关闭在一个进程的运行过程中,不断积累打开的文件,逐渐消耗掉文件描述符表里的内容最终就消耗殆尽了
但是如果进程的生命周期很短,打开一下没多久就关闭了.谈不上泄露
文件资源泄露这样的问题,在服务器这边是比较 严重的, 在客户端这边一般来说影响不大.

 【服务器】

【交互】

1.服务器先启动.服务器启动之后,就会进入循环,执行到 receive 这里并阻塞 (此时还没有客户端过来呢)
2.客户端开始启动,也会先进入 while 循环,执行 scanner.next.并且也在这里阻塞当用户在控制台输入字符串之后,next 就会返回,从而构造请求数据并发送出来~~

3.客户端发送出数据之后,
服务器: 就会从 receive 中返回,进一步的执行解析请求为字符串,执行 process 操作,执行 send 操作
客户端: 继续往下执行,执行到 receive,等待服务器的响应

4.客户端收到从服务器返回的数据之后,就会从 receive 中返回执行这里的打印操作,也就把响应给显示出来了
5.服务器这边完成一次循环之后, 又执行到 receive 这里,客户端这边完成一次循环之后,又执行到 scanner.next 这里双双进入阻塞

 【翻译服务器】

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

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

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

        // 此处可以往这个表里插入几千几万个这样的英文单词.
        dict.put("dog", "小狗");
        dict.put("cat", "小猫");
        dict.put("pig", "小猪");
    }

    // 重写 process 方法, 在重写的方法中完成翻译的过程.
    // 翻译本质上就是 "查表"
    @Override
    public String process(String request) {
        return dict.getOrDefault(request, "该词在词典中不存在!");
    }

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

上述重写 process 方法,就可以在子类中组织你想要的"业务逻辑",(你要写代码解决一些实际的问题)

3.TCP 的 socket api 如何使用 

TCP 的 socket api 和 UDP 的 socket api 差异又很大~,

但是和前面讲的 文件操作,有密切联系的

两个关键的类

1.ServerSocket(给服务器使用的类,使用这个类来绑定端口号)

2.Socket(既会给服务器用,又会给客户端用)

这俩类都是用来表示 socket 文件的,(抽象了网卡这样的硬件设备)

TCP 是字节流的.传输的基本单位,是 byte

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 = null;

    public TcpEchoClient(String serverIp, int serverPort) throws IOException {
        // 需要在创建 Socket 的同时, 和服务器 "建立连接", 此时就得告诉 Socket 服务器在哪里~~
        // 具体建立连接的细节, 不需要咱们代码手动干预. 是内核自动负责的.
        // 当我们 new 这个对象的时候, 操作系统内核, 就开始进行 三次握手 具体细节, 完成建立连接的过程了.
        socket = new Socket(serverIp, serverPort);
    }

    public void start() {
        // tcp 的客户端行为和 udp 的客户端差不多.
        // 都是:
        // 3. 从服务器读取响应.
        // 4. 把响应显示到界面上.
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) {
            PrintWriter writer = new PrintWriter(outputStream);
            Scanner scannerNetwork = new Scanner(inputStream);
            while (true) {
                // 1. 从控制台读取用户输入的内容
                System.out.print("-> ");
                String request = scanner.next();
                // 2. 把字符串作为请求, 发送给服务器
                //    这里使用 println, 是为了让请求后面带上换行.
                //    也就是和服务器读取请求, scanner.next 呼应
                writer.println(request);
                writer.flush();
                // 3. 读取服务器返回的响应.
                String response = scannerNetwork.next();
                // 4. 在界面上显示内容了.
                System.out.println(response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {
        TcpEchoClient client = new TcpEchoClient("127.0.0.1", 9090);
        client.start();
    }
}
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("服务器启动!");
        ExecutorService service = Executors.newCachedThreadPool();
        while (true) {
            // 通过 accept 方法, 把内核中已经建立好的连接拿到应用程序中.
            // 建立连接的细节流程都是内核自动完成的. 应用程序只需要 "捡现成" 的.
            Socket clientSocket = serverSocket.accept();
            // 此处不应该直接调用 processConnection, 会导致服务器不能处理多个客户端.
            // 创建新的线程来调用更合理的做法.
            // 这种做法可行, 不够好
//            Thread t = new Thread(() -> {
//                processConnection(clientSocket);
//            });
//            t.start();

            // 更好一点的办法, 是使用线程池.
            service.submit(new Runnable() {
                @Override
                public void run() {
                    processConnection(clientSocket);
                }
            });
        }
    }

    // 通过这个方法, 来处理当前的连接.
    public void processConnection(Socket clientSocket) {
        // 进入方法, 先打印一个日志, 表示当前有客户端连上了.
        System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
        // 接下来进行数据的交互.
        try (InputStream inputStream = clientSocket.getInputStream();
             OutputStream outputStream = clientSocket.getOutputStream()) {
            // 使用 try ( ) 方式, 避免后续用完了流对象, 忘记关闭.
            // 由于客户端发来的数据, 可能是 "多条数据", 针对多条数据, 就循环的处理.
            while (true) {
                Scanner scanner = new Scanner(inputStream);
                if (!scanner.hasNext()) {
                    // 连接断开了. 此时循环就应该结束
                    System.out.printf("[%s:%d] 客户端下线!\n", clientSocket.getInetAddress(), clientSocket.getPort());
                    break;
                }
                // 1. 读取请求并解析. 此处就以 next 来作为读取请求的方式. next 的规则是, 读到 "空白符" 就返回.
                String request = scanner.next();
                // 2. 根据请求, 计算响应.
                String response = process(request);
                // 3. 把响应写回到客户端.
                //    可以把 String 转成字节数组, 写入到 OutputStream
                //    也可以使用 PrintWriter 把 OutputStream 包裹一下, 来写入字符串.
                PrintWriter printWriter = new PrintWriter(outputStream);
                //    此处的 println 不是打印到控制台了, 而是写入到 outputStream 对应的流对象中, 也就是写入到 clientSocket 里面.
                //    自然这个数据也就通过网络发送出去了. (发给当前这个连接的另外一端)
                //    此处使用 println 带有 \n 也是为了后续 客户端这边 可以使用 scanner.next 来读取数据.
                printWriter.println(response);
                //    此处还要记得有个操作, 刷新缓冲区. 如果没有刷新操作, 可能数据仍然是在内存中, 没有被写入网卡.
                printWriter.flush();
                // 4. 打印一下这次请求交互过程的内容
                System.out.printf("[%s:%d] req=%s, resp=%s\n", clientSocket.getInetAddress(), clientSocket.getPort(),
                        request, response);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                // 在这个地方, 进行 clientSocket 的关闭.
                // processConnection 就是在处理一个连接. 这个方法执行完毕, 这个连接也就处理完了.
                clientSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public String process(String request) {
        // 此处也是写的回显服务器. 响应和请求是一样的.
        return request;
    }

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

【服务器】

内核中有一个“队列”(可以视为阻塞队列)

如果有客户端,和服务器建立连接,这个时候服务器的应用程序是不需要做出任何操作(也没有任何感知的),内核直接就完成了连接建立的流程(三次握手).

完成流程之后,就会在内核的队列中(这个队列是每个 serverSocket 都有一个这样的队列)。

排队应用程序要想和这个客户端进行通信,就需要通过一个 accept 方法把内核队列里已经建立好的连接对象,拿到应用程序中。

【question】

前面写过的 DatagramSocket, ServerSocket 都没写 close, 但是我们说这个东西都没关系但是 clientSocket 如果不关闭,就会真的泄露了!!!
DatagramSocket 和 ServerSocket,都是在程序中,只有这么一个对象.申明周期, 都是贯穿整个程序的.

而ClientSocket 则是在循环中,每次有一个新的客户端来建立连接,都会创建出新的clientSocket

每次执行这个,都会创建新的 clientSocket,并且这个 socket 最多使用到 该客户端退出(断开连接)
此时,如果有很多客户端都来建立连接~~此时,就意味着每个连接都会创建 clientSocket.当连接断开clientSocket 就失去作用了,但是如果没有手动 close此时这个 socket 对象就会占据着文件描述符表的位置

【客户端】

【question】出现一个bug

当前启动两个客户端,同时连接服务器.
其中一个客户端(先启动的客户端) 一切正常.
另一个客户端 (后启动的客户端)则没法和服务器进行任何交互,(服务器不会提示"建立连接”,也不会针对 请求 做出任何响应)

上述bug和代码结构密切相关

确实如刚才推理的现象一样,第一个客户端结束的时候,就从 processConnection 返回了就可以执行到第二次 accept 了,也就可以处理第二个客户端了~~
很明显,如果启动第三个客户端,第三个客户端也会僵硬住,又会需要第二个客户端结束才能活过来...

如何解决上述问题?让一个服务器可以同时接待多个客户端呢??

关键就是,在处理第一个客户端的请求的过程中,要让代码能够快速的第二次执行到 accept ~~~【多线程】

上述这里的关键,就是让这两个循环能够"并发"执行.
各自执行各自的,不会因为进入一个循环影响到另一个~~

【刚才出现这个问题的关键在于两重循环在一个线程里进入第二重循环的时候,无法继续执行第一个循环.
Udp 版本的服务器,当时是只有一个循环,不存在类似的问题~~(前面部署到云服务器的时候)】

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简单案例验证说明 双亲委派机制

双亲委派介绍 双亲委派机制&#xff08;Parent Delegation Mechanism&#xff09;是Java中的一种类加载机制。在Java中&#xff0c;类加载器负责加载类的字节码并创建对应的Class对象。双亲委派机制是指当一个类加载器收到类加载请求时&#xff0c;它会先将该请求委派给它的父…

aysnc-await的用法

aysnc-await是promise的一种特殊语法&#xff0c;它可以更简洁的得到promise aysnc function aysnc 放在函数前定义函数&#xff0c;它规定了这个函数的返回值一定为promise&#xff0c; // 通过new新建一个promise(旧) // let p new Promise(function(resolve,reject){ //…

可搜索加密:保护隐私的搜索技术

在信息化、数字化快速发展的今天&#xff0c;数据的安全性和隐私性已成为公众关注的焦点。随着云计算、大数据等技术的广泛应用&#xff0c;数据共享与协同工作日益普遍&#xff0c;但如何在确保数据安全性的前提下&#xff0c;实现数据的快速、高效检索&#xff0c;成为了一个…

MySQL中explain的用法

执行结果各字段的含义 EXPLAIN SQL语句 如&#xff1a; EXPLAIN SELECT * FROM test 执行结果&#xff1a; 列名描述id在一个大的查询语句中每个SELECT关键字都对应一个 唯一的idselect_typeSELECT关键字对应的那个查询的类型table表名partitions匹配的分区信息type针对单表…

LLama的激活函数SwiGLU 解释

目录 Swish激活函数 1. Swish函数公式 LLaMA模型中的激活函数 1. SwiGLU激活函数 2. SwiGLU激活函数的表达式 3. SwiGLU激活函数的优势 Swish激活函数 Swish是一种激活函数&#xff0c;其计算公式如下&#xff1a; 1. Swish函数公式 Swish(x) x * sigmoid(x) 其中&am…

基于CANoe从零创建以太网诊断工程(2)—— TCP/IP Stack 配置的三种选项

&#x1f345; 我是蚂蚁小兵&#xff0c;专注于车载诊断领域&#xff0c;尤其擅长于对CANoe工具的使用&#x1f345; 寻找组织 &#xff0c;答疑解惑&#xff0c;摸鱼聊天&#xff0c;博客源码&#xff0c;点击加入&#x1f449;【相亲相爱一家人】&#x1f345; 玩转CANoe&…

Confluence 快捷键大揭秘:提高效率的小窍门

使用 Confluence 快捷键的好处有&#xff1a; 1.提高工作效率&#xff1b; 2.更流畅地进行编辑、导航和管理操作&#xff1b; 3.减少误操作&#xff1b; 4.展现专业水平。 更多精彩内容&#xff1a; 成为 Jira 大师&#xff1a;效率达人的必备秘诀 Jira Cloud 项目管理专栏 PMO…

怎样把PDF分割成多个文件?有哪些方法可以分割PDF文件?这几个方法成功率很高!

一&#xff0c;引言 PDF分割&#xff0c;即将一个完整的PDF文档拆分为多个较小的部分&#xff0c;是许多用户在处理 PDF文件时经常需要执行的操作。无论是为了单独提取某个章节、创建电子书章节、还是为了在多个设备间轻松共享&#xff0c;PDF分割都显得非常实用。本文将详细介…

AI大模型语音实时对话聊天机器人实现:ollama、funasr;支持语音实时语音打断;回音消除噪声抑制

ASR:funasr(1.0.19) LLM:ollama(Qwen) TTS(edge_tts) 支持语音实时语音打断:这是通过子进程的控制创建与杀掉,这里是通过有人再次说话就打断tts 回音消除噪声抑制:喇叭的tts播报影响到麦克风的识别了,播报的声音被错误的识别;这里可以jd买个回音消除的麦克风设备;或者有…

python 10实验

实验内容&#xff1a; 使用线性回归算法预测儿童身高 实验目的&#xff1a; 理解线性回归算法的原理&#xff0c;了解线性回归算法适用的问题类型&#xff0c;能够使用线性回归算法解决问题 实验内容&#xff1a; 一个人的身高除了随年龄变大而增长以外&#xff0c;在一定程…

revit\navisworks各种安装问题

You have entered a nonvalid serial number &#xff0c;怎么都不给你一个机会输出序列号&#xff0c;怎么办&#xff1f; step1: C:\Program Files (x86)\Common Files\Autodesk Shared\AdskLicensing目录下找到uninstall.exe&#xff0c;右键管理员模式运行&#xff0c;会…

动态活码二维码怎么制作?在线二维码生成器的使用技巧

二维码是如何生成的呢&#xff1f;现在二维码与我们的工作和生活息息相关&#xff0c;越来越多的场景都会有不同类型的二维码&#xff0c;比如常见的有视频、图片、文件、问卷、文本等等类型的内容。面对不同用途需求来制作二维码来为其他人提供内容展示&#xff0c;提升用户获…

Chisel 入门(2)运算符

Chisel 入门(2) 运算符 逻辑运算符 ChiselExplanationwidth!x逻辑非1x && y逻辑与1x||y逻辑或1 位操作运算符 ChiselExplanationwidthin Verilog~x位反w(x)~ signal_xx & y位与max(w(x), w(y))signal_x & signal_yx | y位或max(w(x), w(y))signal_x | sign…

Oracle数据库的AI能力分析,释放企业数据价值

解锁Oracle数据库的AI潜力 Oracle数据库提供了一系列的AI能力&#xff0c;旨在帮助企业和开发者更高效地利用人工智能技术。以下是Oracle数据库AI能力的一些关键点&#xff1a;1. AI向量相似性搜索&#xff1a;Oracle Database 23c引入了AI Vector Search功能&#xff0c;该功…

基于B2C的网上拍卖系统——秒杀与竞价

点击下载源码和论文https://download.csdn.net/download/liuhaikang/89222887 课题背景及意义 随着网络的进一步普及和电子商务的高速发展&#xff0c;越来越多的人们开始在网络中寻求方便。网上网物具备了省时、省事、省心、高效等特点&#xff0c;从而受到越来越多人的欢迎。…

SpringCloud系列(16)--将服务提供者Provider注册进Zookeeper

前言&#xff1a;在上一章节中我们说明了一些关于Eureka自我保护模式&#xff0c;而且自上一章节起关于Eureka的知识已经讲的差不多了&#xff0c;不过因为Eureka已经停更了&#xff0c;为了安全考虑&#xff0c;我们要用还在更新维护的注册中心来取代Eureka&#xff0c;而本章…

C语言:复习

文章目录 思维导图数组和指针库函数的模拟实现判断大小端 最近知识学的差不多了&#xff0c;因此开始复习&#xff0c;本篇开始的是对于C语言的复习 思维导图 下面就依据下图&#xff0c;进行内容的整理 数组和指针 这个模块算是C语言中比较大的一个模块了&#xff0c;具体概…

Three.js——基础材质、深度材质、法向材质、面材质、朗伯材质、Phong材质、着色器材质、直线和虚线、联合材质

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

如何使用rdtsc和C/C++来测量运行时间(如何使用内联汇编和获取CPU的TSC时钟频率)

本文主要是一个实验和思维扩展&#xff0c;是一个初步的尝试&#xff0c;旨在研究一些时间测量实现和在 C/C 中内联汇编和汇编函数的使用方法。除非你有特殊用途&#xff0c;不然不要使用汇编指令来实现这个功能。在“扩展阅读”部分会列出了一些不需要内联汇编实现的方法。 写…

猫头虎分享已解决Bug || TypeError: Cannot read property ‘map‘ of undefined**

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …