JavaEE-网络编程

news2024/12/23 3:27:48

目录

  • 一、网络编程套接字
  • 二、UDP Socket
    • 2.1 客户端服务器程序-回显服务(EchoServer)
      • 2.1.1 UdpEchoServer
      • 2.1.2 UdpEchoClient
      • 2.1.3 一个简单程序
  • 三、TCP 客户端服务器程序
    • 3.1 TCP API

一、网络编程套接字

网络编程套接字就是操作系统给应用程序提供的一组API(叫做socket API)。

socket 可以视为是应用层和传输层之间的通信桥梁。

传输层的核心协议有两种:TCP和UDP。

TCP:有连接;可靠传输;面向字节流;全双工;

UDP:无连接;不可靠传输;面向数据报;全双工;

  1. 有连接和无连接
    有连接:像打电话,得先接通,才能交互数据
    无连接:像发微信,不需要接通,直接就能发数据

  2. 可靠传输与不可靠传输
    可靠:传输过程中,发送方知道接收方有没有收到数据
    不可靠:参考无连接,微信直接可以发消息,不知道对方有没有看见这个消息。

  3. 面向字流/数据报
    面向字节流:以字节为单位进行传输,类似于文件操作中的字节流。
    面向数据报:以数据报为单位进行传输,一个数据报会明确大小,一次发送/接收一个完整的数据报,不能是半个数据报。

  4. 全双工/半双工
    全双工:一条链路,双向通信。
    半双工:一条链路,单向通信。

二、UDP Socket

UDP Socket 中主要涉及两个类:DatagramSocket 和 DatagramPacket.

DatagramSocket 的一个对象就对应操作系统中的一个socket文件。

DatagramPacket代表了一个UDP数据报,使用UDP传输数据的基本单位。
在这里插入图片描述

2.1 客户端服务器程序-回显服务(EchoServer)

回显:请求内容是啥,得到的响应就是啥。
在这里插入图片描述
这里在我们的idea下先建立一个包network,在这个包下建立两个类分别是服务器UdpEchoServer和客户端UdpEchoClient。

2.1.1 UdpEchoServer

  1. 进行网络编程,第一步就需要先准备好 socket实例,这是进行网络编程的大前提
private DatagramSocket socket = null;
  1. 此处在构造服务器这边的socket对象的时候,就需要显式的绑定一个端口号
    端口号是用来区分一个应用程序的,主机收到网卡上的数据的时候,这个数据该给哪个程序?
    抛出异常的原因:1.端口号可能已经被占用了。2.每个进程能够打开的文件个数是有限的。
public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
  1. 启动服务器,服务器是被动接收请求的一方,主动发送请求的是客户端,DatagramPacket刚才说过是表示一个UDP数据报,发送一次数据就是发送DatagramPacket,接收一次数据也就是在收一个DatagramPacket。
// 3. 把响应写回到客户端。
            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);

        }
    }

    // 由于是回显服务,响应就和请求一样了
    // 实际上对于一个真实的服务器来说,这个过程是复杂的,为了实现这个过程,可能需要几行甚至几万行
    private String process(String request) {
        return request;
    }
  • 这里requestPacket.getLength()这个长度不一定是1024,可能此处的UDP数据报最长是1024,实际的数据可能不够1024。
  • 注意这里send方法的参数,也是DatagramPacket,需要把响应数据先构造成一个DatagramPacket再进行发送。
  • response.getByte()这里的参数也不再是一个空的数组,response是刚才根据请求计算得到的响应。DatagramPacket里面的数据就是String response的数据。
  • requestPacket.getSocketAddress();这个参数的作用就是表示要把数据发给哪个地址+端口。
  • 完整代码:

    package network;
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.SocketException;
    
    @SuppressWarnings({"all"})
    public class UdpEchoServer {
        // 进行网络编程,第一步就需要先准备好 socket实例,这是进行网络编程的大前提
        private DatagramSocket socket = null;
    
        public UdpEchoServer(int port) throws SocketException {
            socket = new DatagramSocket(port);
        }
    
        // 启动服务器
        public void start() throws IOException {
            System.out.println("启动服务器");
            // UDP 不需要建立连接,直接接收从客户端来的数据即可~
            while (true) {
                // 1. 读取客户端发来的请求
                DatagramPacket requestPacket = new DatagramPacket(new byte[1024], 1024);
                socket.receive(requestPacket);// 为了接收数据,需要先准备一个空的DatagramPacket对象,由receive来进行填充数据
                // 把 DatagramPacket 解析成一格String
                String request = new String(requestPacket.getData(), 0, requestPacket.getLength(), "utf-8");
                // 2. 根据请求计算响应( 由于是一个回显服务器,2省略)
                String response = process(request);
                // 3. 把响应写回到客户端。
                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);
    
            }
        }
    
        // 由于是回显服务,响应就和请求一样了
        // 实际上对于一个真实的服务器来说,这个过程是复杂的,为了实现这个过程,可能需要几行甚至几万行
        private String process(String request) {
            return request;
        }
    
        public static void main(String[] args) throws IOException {
            UdpEchoServer server = new UdpEchoServer(9090);
            server.start();
        }
    }
    
    

    在这里插入图片描述

    2.1.2 UdpEchoClient

    在客户端构造 socket 对象的时候,就不再手动指定端口号,使用无参版本的构造方法。
    不指定端口号,是让操作系统自己分配一个空闲的端口号。
    在这里插入图片描述

    package network;
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    import java.net.SocketException;
    import java.util.Scanner;
    
    @SuppressWarnings({"all"})
    public class UdpEchoClient {
        private DatagramSocket socket = null;
        private String serverIp;
        private int serverPort;
    
        public UdpEchoClient(String ip, int port) throws SocketException {
            socket = new DatagramSocket();
            serverIp = ip;
            serverPort = port;
        }
    
        public void start() throws IOException {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                // 1. 先从控制台读取一个用户输入的字符串
                System.out.println("-> ");
                String request = scanner.next();
                // 2. 把这个用户输入的内容构造成一格UDP请求,并发送
                //    构造的请求里包含两部分信息:
                //    1) 数据的内容,request 字符串
                //    2) 数据要发送给谁  服务器的IP + 端口
                DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                        InetAddress.getByName(serverIp),serverPort);
                socket.send(requestPacket);
                // 3. 从服务器读取响应数据,并解析
                DatagramPacket responsePacket = new DatagramPacket(new byte[1024],1024);
                socket.receive(responsePacket);
                // 4. 把响应结果显示到控制台上。
                String response = new String(responsePacket.getData(),0,responsePacket.getLength(),"UTF-8");
                System.out.printf("req: %s, resp: %s\n",request,response);
    
            }
        }
    
        public static void main(String[] args) throws IOException {
            UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
            client.start();
        }
    }
    
    

    在这里插入图片描述

    在这里插入图片描述

    2.1.3 一个简单程序

    写一个翻译程序(英译汉)

    package network;
    
    import java.io.IOException;
    import java.net.SocketException;
    import java.util.HashMap;
    
    @SuppressWarnings({"all"})
    public class UdpDictServer extends UdpEchoServer{
    
        private HashMap<String, String> dict = new HashMap<>();
    
        public UdpDictServer(int port) throws SocketException {
            super(port);
    
            // 简单构造几个词
            dict.put("cat","小猫");
            dict.put("dog","小狗");
            dict.put("hello","你好");
            dict.put("pig","小猪");
        }
    
        @Override
        public String process(String request) {
            return dict.getOrDefault(request, "该词无法被翻译");
        }
    
        public static void main(String[] args) throws IOException {
            UdpEchoServer server = new UdpEchoServer(9090);
            server.start();
        }
    }
    
    

    在这里插入图片描述

    在这里插入图片描述

    三、TCP 客户端服务器程序

    3.1 TCP API

    在TCP API 中,也是涉及到两个核心的类,
    ServerSocket(专门给TCP服务器用)
    Socket(既需要给服务器用,又需要给客户端用)

    前面基本都和UDP的差不多,区别就是这里的start方法,由于tcp是有连接的,不是一上来就能读数据,需要先建立连接(打电话);accept返回了一个socket对象,accept可以理解为接电话,那么接电话的前提就是有人给你打电话,如果无,那么这里accept就会阻塞。

    TcpEchoServer

    // listen -> 监听
        // 但是在 Java socket 中是体现不出来"监听"的含义的
        // 之所以这么叫,是操作系统原生的API 中有一个操作叫做listen
        private ServerSocket listenSocket = null;
    
        public TcpEchoServer(int port) throws IOException {
            listenSocket = new ServerSocket(port);
        }
    
        public void start() throws IOException {
            System.out.println("服务器启动!");
            while (true) {
                // 由于 Tcp是有连接的,不能一上来就读取数据,而要先建立连接(接电话)
                // accept 就是在"接电话",前提是有人给你打电话
                // accept 返回了一个socket 对象,称为clientSocket,后续和客户端的沟通都是通过clientSocket来完成的
                // 进一步讲,listenSocket就干了一件事,就是接电话
                Socket clientSocket =  listenSocket.accept();
                processConnection(clientSocket);
            }
        }
    

    接下来我们来写processConnection()方法的代码。

    private void processConnection(Socket clientSocket) throws IOException {
            System.out.printf("[%s:%d] 客户端建立连接!", clientSocket.getInetAddress().toString(), clientSocket.getPort());
            // 接下来处理请求和响应
            // 这里的针对 TCP socket 的读写就和文件读写是一模一样的
            try (InputStream inputStream = clientSocket.getInputStream()) {
                try (OutputStream outputStream = clientSocket.getOutputStream()) {
                    // 循环的处理每个请求,分别返回响应
                    Scanner scanner = new Scanner(System.in);
                    while (true) {
                        // 1. 读取请求
                        if (!scanner.hasNext()) {
                            System.out.printf("[%s:%d] 客户端断开连接!", clientSocket.getInetAddress().toString(), clientSocket.getPort());
                            break;
                        }
                        // 此处用 Scanner 更方便,如果不用Scanner 就用原生的InputStream的read
                        String request = scanner.next();
                        // 2. 根据请求,计算响应
                        String response = process(request);
                        // 3. 把这个响应返回客户端
                        // 为了方便响应,可以使用PrintWriter 把OutputStream包裹一下
                        PrintWriter printWriter = new PrintWriter(outputStream);
                        printWriter.println(response);
                        // 刷新缓冲区,如果没有这个刷新,可能客户端就不能第一时间看到响应
                        printWriter.flush();
    
                        System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response);
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                clientSocket.close();
            }
        }
    
        private String process(String request) {
            return request;
        }
    

    完整代码:

    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;
    
    @SuppressWarnings({"all"})
    public class TcpEchoServer {
        // listen -> 监听
        // 但是在 Java socket 中是体现不出来"监听"的含义的
        // 之所以这么叫,是操作系统原生的API 中有一个操作叫做listen
        private ServerSocket listenSocket = null;
    
        public TcpEchoServer(int port) throws IOException {
            listenSocket = new ServerSocket(port);
        }
    
        public void start() throws IOException {
            System.out.println("服务器启动!");
            while (true) {
                // 由于 Tcp是有连接的,不能一上来就读取数据,而要先建立连接(接电话)
                // accept 就是在"接电话",前提是有人给你打电话
                // accept 返回了一个socket 对象,称为clientSocket,后续和客户端的沟通都是通过clientSocket来完成的
                // 进一步讲,listenSocket就干了一件事,就是接电话
                Socket clientSocket = listenSocket.accept();
                processConnection(clientSocket);
            }
        }
    
        private void processConnection(Socket clientSocket) throws IOException {
            System.out.printf("[%s:%d] 客户端建立连接!", clientSocket.getInetAddress().toString(), clientSocket.getPort());
            // 接下来处理请求和响应
            // 这里的针对 TCP socket 的读写就和文件读写是一模一样的
            try (InputStream inputStream = clientSocket.getInputStream()) {
                try (OutputStream outputStream = clientSocket.getOutputStream()) {
                    // 循环的处理每个请求,分别返回响应
                    Scanner scanner = new Scanner(System.in);
                    while (true) {
                        // 1. 读取请求
                        if (!scanner.hasNext()) {
                            System.out.printf("[%s:%d] 客户端断开连接!", clientSocket.getInetAddress().toString(), clientSocket.getPort());
                            break;
                        }
                        // 此处用 Scanner 更方便,如果不用Scanner 就用原生的InputStream的read
                        String request = scanner.next();
                        // 2. 根据请求,计算响应
                        String response = process(request);
                        // 3. 把这个响应返回客户端
                        // 为了方便响应,可以使用PrintWriter 把OutputStream包裹一下
                        PrintWriter printWriter = new PrintWriter(outputStream);
                        printWriter.println(response);
                        // 刷新缓冲区,如果没有这个刷新,可能客户端就不能第一时间看到响应
                        printWriter.flush();
    
                        System.out.printf("[%s:%d] req: %s, resp: %s\n", clientSocket.getInetAddress().toString(), 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 server = new TcpEchoServer(9090);
            server.start();
        }
    }
    
    

    TcpEchoClient

    package network;
    
    import jdk.internal.util.xml.impl.Input;
    import org.omg.Messaging.SYNC_WITH_TRANSPORT;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.util.Scanner;
    
    @SuppressWarnings({"all"})
    public class TcpEchoClient {
        // 用普通的 socket 即可,不用 ServerSocket了
        // 此处也不用手动给客户端指定端口号,让系统自由分配
        private Socket socket = null;
    
        public TcpEchoClient(String sreverIP, int sreverPort) throws IOException {
            // 其实这里是可以给的,但是这里给了之后,含义是不同的
            // 这里传入的ip和端口号的含义表示的不是自己绑定,而是表示和这个ip端口建立连接口
            socket = new Socket(sreverIP, sreverPort);
        }
    
        public void start() {
            System.out.println("和服务器连接成功!");
            Scanner scanner = new Scanner(System.in);
            try (InputStream inputStream = socket.getInputStream()) {
                try (OutputStream outputStream = socket.getOutputStream()) {
                    while (true) {
                        // 要做的事情,仍然是四个步骤
                        // 1. 从控制台读取字符串
                        System.out.println("-> ");
                        String request = scanner.next();
                        // 2. 根据读取的字符串, 构造请求,把请求发给服务器
                        PrintWriter printWriter = new PrintWriter(outputStream);
                        printWriter.println(request);
                        printWriter.flush();// 如果不刷新,服务器可能不能及时看到数据
                        // 3. 从服务器读取响应,并解析
                        Scanner respScanner = new Scanner(System.in);
                        String response = respScanner.next();
                        // 4. 把结果显示到控制台上
                        System.out.printf("req: %s, resp: %s\n",request, response);
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
    
        }
        
        public static void main(String[] args) throws IOException {
            TcpEchoClient client = new TcpEchoClient("127.0.0.1",9090);
            client.start();
        }
    }
    
    

    先启动服务器,再启动客户端
    在这里插入图片描述
    服务器启动成功,我们启动客户端,可以发现和服务器连接成功!在这里插入图片描述
    然后我们点到服务器这边观察一下,可以发现服务器已经和客户端建立连接,系统已经分配了端口号。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    那么我们结束客户端,看服务器这边的响应。
    在这里插入图片描述
    在这里插入图片描述
    我们可以发现这里的代码第一次accept结束之后,就会进入processConnection,在processConnection中又有一个循环,若processConnection里面的循环不停,processConnection就无法完成,就会导致外层循环无法进入下一轮,也就无法第二次调用accept了。

    我们可以通过多线程的方式来解决这个问题,让主线程循环调用accept,当有客户端连接上来的时候就让主线程创建一个新线程,由新线程负责客户端的若干个请求,这个时候多个线程看上去是同时执行的。

    这里我们新写一个类TcpThreadEchoServer,在原有的TcpEchoServer基础上修改以下部分代码即可。

         Thread t = new Thread(()->{
             processConnection(clinetSocket);
         });
         t.start();
    

    TcpThreadEchoServer

    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 TcpThreadEchoServer {
        //    private ServerSocket listenSocket = null;
        private   ServerSocket serverSocket = null;
     
        public  TcpThreadEchoServer(int port) throws IOException {
            serverSocket = new ServerSocket(port);
        }
     
        public void start() throws IOException {
            System.out.println("服务器启动!");
            while (true){
                Socket clinetSocket = serverSocket.accept();
                Thread t = new Thread(()->{
                    processConnection(clinetSocket);
                });
                t.start();
            }
        }
     
        private void processConnection(Socket clinetSocket) {
            System.out.printf("[%s:%d客户端建立连接!\n",clinetSocket.getInetAddress().toString(),clinetSocket.getPort());
            try(InputStream inputStream = clinetSocket.getInputStream()){
                try(OutputStream outputStream = clinetSocket.getOutputStream()){
                    Scanner scanner = new Scanner(inputStream);//读取请求
                    while (true){
                        if(!scanner.hasNext()){
                            System.out.printf("[%s:%d]客户端断开连接!\n",clinetSocket.getInetAddress().toString(),clinetSocket.getPort());
                            break;
                        }
                        String request = scanner.next();
                        String response = process(request);
                        PrintWriter printWriter = new PrintWriter(outputStream);
                        printWriter.println(response);
                        //刷新缓冲区,如果没有这个刷新,可能客户端就不能第一时间看到响应结果。
                        printWriter.flush();
                        System.out.printf("[%s:%d] rep:%s,resp:%s\n",
                                clinetSocket.getInetAddress().toString(),clinetSocket.getPort(),request,response);
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                //记得关闭操作
                try {
                    clinetSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
     
        private String process(String request) {
            return request;
        }
     
        public static void main(String[] args) throws IOException {
            TcpThreadEchoServer server = new TcpThreadEchoServer(9090);
            server.start();
        }
    }
     
    

    那么之前为什么UDP版本的程序就不需要多线程就可以处理多个请求呢?

    因为UDP不需要连接,只需要一个循环就可以处理所有客户端的请求,但是TCP即需要处理连接,又需要处理一个连接中的多个请求。

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

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

相关文章

NLP学习笔记(七) BERT简明介绍

大家好&#xff0c;我是半虹&#xff0c;这篇文章来讲 BERT\text{BERT}BERT (Bidirectional Encoder Representations from Transformers) 原始论文请戳这里 0 概述 从某种程度上来说&#xff0c;深度学习至关重要的一环就是表征学习&#xff0c;也就是学习如何得到数据的向…

怎么把两个PDF合并?教你们几个简单的方法

不知道大家平时处理文件的数量多不多&#xff0c;但是小编日常处理文件真的特别多&#xff0c;所以小编经常会使用专业的格式转换器来处理文件&#xff0c;这样就可以高效处理文件了&#xff0c;例如我们需要将多个PDF文件合并&#xff0c;这样就只需要传输一个文件就可以了&am…

自定义starter解决请求绕过网关问题

引言 微服务项目中网关是一个常见的模块&#xff0c;通过网关的分发可以实现负载均衡、鉴权等操作&#xff1b;但是搭建好网关可以发现&#xff0c;虽然可以通过网关端口请求后端&#xff0c;如果有其他服务的地址依然可以使用其他服务地址绕过网关请求&#xff0c;这里我提供…

利用RadminLan和TcpRoute2将工作带回家

需要准备的工具 1.RadminLan 下载地址–>https://www.radmin-lan.cn/ 2.TcpRoute2 项目地址–>https://github.com/GameXG/TcpRoute2 *选用&#xff1a;浏览器插件proxy-switchyomega&#xff1a;https://microsoftedge.microsoft.com/addons/detail/proxy-switchyomega…

Visual Studio Code 的安装和使用

Visual Stuio Code 微软出的一款免费编辑器。 有 Windows、Linux 和macOS 三种版本的&#xff0c;属于跨平台的编辑器。它功能强大&#xff0c;支持插件工具安装&#xff0c;对于写代码、阅读代码的人来说是非常方便的。 1、安装 Visual Stuio Code 下载地址如下&#xff1a; h…

win10修改jdk版本之后不生效的有效解决方法

问题起因今天学习seata的时候&#xff0c;启动seata服务发现启动不了报下图错误。发现是自己jdk版本太高了&#xff0c;现在我用的是jdk17。然后我修改jdk的环境变量&#xff0c;确定保存好。发现jdk的版本还是没有变化。问题原因当使用安装版本的JDK程序时&#xff08;一般是1…

jmeter 并发测试

1.右键测试计划(Test plan), 添加线程组 2.线程组配置 3.右键线程组, 添加取样器-HTTP请求 4.HTTP请求配置 5. 添加查看结果树(也可以在Test plan 测试计划上右键添加)

Grafana 系列文章(二):使用 Grafana Agent 和 Grafana Tempo 进行 Tracing

&#x1f449;️URL: https://grafana.com/blog/2020/11/17/tracing-with-the-grafana-cloud-agent-and-grafana-tempo/ ✍Author: Robert Fratto • 17 Nov 2020 &#x1f4dd;Description: Heres your starter guide to configuring the Grafana Agent to collect traces and…

【刷题】多数元素

这是leetcode第169题的解答。 目录 一、多数元素 二、实现思路 1.排序中间下标求众数 2.投票法 总结 一、多数元素 多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 二、实现思路 1.排序中间下标求众数 原理&#xff1a; 通过排序使得数组有序&#xff0c;因为多数元素…

ESP32设备驱动-TM1637-驱动4位7段数码管

TM1637-驱动4位7段数码管 1、TM1637介绍 TM1637是一款带键盘扫描接口的LED(发光二极管显示器)驱动控制专用电路,内部集成了MCU数字接口、数据锁存、LED高压驱动、键盘扫描等功能。 TM1637使用DIP20/SOP20封装,主要适用于电磁炉、微波炉、小家电的显示驱动。 TM1637有如下…

【C++】初识C++

本期博客我们来正式进入到期待已久C嘎嘎的学习希望C语言以后别给我打电话了&#xff0c;我怕C误会&#x1f63c;一、认识C1. 什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的 程序&#xff0c;需要高度的抽象和…

蓝奥声无线单火控制技术在单火开关应用中的优势

随着科技的发展&#xff0c;智能产品在生活中越来越常见&#xff0c;为方便业主使用&#xff0c;就连开关也有了高阶智能版&#xff0c;据相关专家介绍&#xff0c;智能开关主要分为单火和零火两种&#xff0c;很多非专业人士搞不明白&#xff0c;但又害怕因此选择失误。那么&a…

关于微服务的一些总结和经验之谈,来看看你都了解吗

文章目录一 谈谈对微服务的理解1. 什么微服务&#xff1f;2. 微服务体系3. 微服务优点4. 微服务缺点5. 什么是gRPC&#xff1f;6. ProtoBuf协议好处&#xff1f;7. gPRC和ProtoBuf联系&#xff1f;二 本次微服务项目学习流程梳理三 微服务项目一般开发流程梳理四 从本次微服务项…

数据结构 | 图结构的讲解与模拟实现 | DFS与BFS的实现

文章目录前言常见概念总结图的模拟实现邻接矩阵和邻接表的优劣图的模拟实现&#xff08;邻接表&#xff09;广度优先遍历&#xff08;BFS&#xff09;深度优先遍历&#xff08;DFS&#xff09;前言 在聊图的结构之前&#xff0c;我们可以先从熟悉的地方开始&#xff0c;这有一…

Leetcode 剑指 Offer II 012. 左右两边子数组的和相等

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer&#xff08;专项突击版&#xff09;系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给你一个整数数组 nums &#xff0c;请计算数组的 中心下标 。 …

Android 启动速度优化

Android 启动速度优化前序统计adb测量手动打点方案预加载class字节码的预加载Activity预创建Glide预初始化WebView预加载数据预加载三方库初始化布局方面ViewStub标签减少层级主题的选择约束布局使用X2C方案过度绘制如何检测过度绘制如何监控页面的渲染速度移除多余背景Recycle…

Linux系列 Linux常用命令(2)

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.Linux常用命令后续 1.创建目录和文件 &#xff08;1&#x…

DaVinci:Camera Raw(Blackmagic RAW)

本文主要介绍 Blackmagic Raw 格式&#xff08;.braw&#xff09;素材相关的 Camera Raw 参数。解码质量Decode Quality解码质量决定了图像解拜耳之后所呈现的素质。默认为“使用项目设置” Use project setting&#xff0c;表示使用项目设置对话框中的“Camera RAW”解码质量设…

PPT录制视频的方法有哪些?分享5种亲测好用的方法

PPT文稿可以通过图文或者动画的形式&#xff0c;直观形象地把内容展现给观众&#xff0c;从而给观众留下深刻的印象。比如老师讲课时会用到PPT&#xff0c;公司开会时也会用到PPT。除了需要使用到PPT文稿之外&#xff0c;有时还要对它进行录制。那你知道PPT录制视频的方法有哪些…

[JavaWeb]HTML

目录1.简介1.1 HTML基本结构1.2 标签使用细节2.常用标签使用2.1 font 字体标签2.2 字符实体2.3 标题标签2.4 超链接标签2.5 有序无序标签(1)无序列表ul/li(2)有序列表ol/li2.6 图像标签(img)2.7 表格(table)标签表格标签-跨行跨列表格2.8 form(表单)标签介绍2.9 input单选多选标…