第1章 手写WebServer

1.1 Web原理

1.1.1 Web概述

        Web是指互联网上的万维网(World Wide Web),是一个由超文本、超链接和多媒体内容组成的信息空间。Web的基础技术是HTTP协议、URL、HTML、CSS和JavaScript等。Web被广泛应用于信息检索、在线购物、社交媒体、在线游戏、在线视频和音乐等领域。

        Web的好处如下:

  • 全球范围的信息共享:Web使得人们可以通过互联网共享信息、知识和文化。用户可以在全球范围内获取和分享信息,这为人们提供了前所未有的便利。
  • 便利的在线服务:Web使得人们可以轻松地访问在线服务,如电子邮件、社交媒体、在线银行、在线购物和在线学习等。这些服务可以大大提高人们的生产力和便利性。
  • 多媒体内容的呈现:Web使得多媒体内容,如图像、视频和音频,可以轻松地在互联网上呈现和传播。这些内容不仅丰富了用户的体验,也为教育和娱乐等领域提供了新的机会。
  • 云计算和Web应用程序:Web应用程序可以在云计算环境中运行,使得用户可以使用网络浏览器轻松访问和使用这些应用程序。这些应用程序包括在线办公套件、在线协作工具、电子商务网站等。

        Web作为一种重要的信息和娱乐渠道,它可以为用户提供全球信息共享、便利的在线服务、多媒体内容的呈现、云计算和Web应用程序等。

1.1.2 Web工作原理

        Web的工作原理是基于客户端-服务器模型(B/S)的。简单来说,Web由Web服务器、Web客户端和通信协议组成。

1、Web服务器

        Web服务器是一个可以接收客户端请求的软件程序。它运行在一个计算机上,一般是指提供Web服务的主机,可以在这个主机上存储Web页面、图像和其他资源。当Web服务器接收到一个客户端请求后,它会发送一个HTTP响应,包括被请求资源的内容和元数据。

2、Web客户端

        Web客户端是通过浏览器访问Web的用户设备,如电脑、手机等。当用户在浏览器中输入URL时,浏览器会发送一个HTTP请求到Web服务器。Web服务器接收到请求后,会查找请求的资源并将响应返回给浏览器,浏览器会将响应显示在用户的屏幕上。

3、HTTP协议

        Web的通信是基于HTTP协议进行的。HTTP是一种客户端-服务器协议,用于传输超文本文档(HTML、XML、图片等)。它定义了浏览器和Web服务器之间的请求和响应交互方式。当浏览器发送HTTP请求时,请求会包含HTTP方法(GET、POST、PUT等)、请求的URL和HTTP头部信息。Web服务器会解析HTTP请求并生成HTTP响应。HTTP响应会包括状态码、HTTP头部信息和响应正文。状态码表示请求是否成功,HTTP头部信息包含了响应的元数据,响应正文则包含了请求的数据。

4、HTML

        HTML是用于创建Web页面的标记语言。HTML标签描述了文本和其他内容在Web页面上的显示方式。浏览器可以读取HTML文件,并将其转换成可视化的Web页面。

        因此,Web的工作原理是基于HTTP协议和客户端-服务器模型的。Web服务器接收HTTP请求,查找并生成响应,并将其发送回浏览器。浏览器读取响应并将其转换成可视化的Web页面。

1.2 手写WebServer

1.2.1 手写WebServer的意义

        手写WebServer对学习编程有很大的意义:有助于深入理解Web的工作原理和HTTP协议的细节,可以提高对计算机网络和操作系统的理解,并增强编程和软件开发的能力。

        1、理解Web工作原理:通过编写代码,实现客户端和服务端的信息交互,从而理解Web的工作原理和HTTP通信协议,掌握Web应用程序和网络通信的底层原理。

        2、提高编程能力:手写WebServer需要掌握网络编程、操作系统和Web开发等多种技能,有利于更好地理解软件开发的基本原理和技术,并掌握高效编程的方法和技巧。

        3、加强调试能力:手写WebServer需要不断地测试和调试,从而加强调试能力,有助于更好地掌握软件调试的方法和技巧,从而提高开发效率和代码质量。

        4、培养创新意识:手写WebServer需要不断地思考和创新,可以培养创新意识,并学会在开发过程中不断提高自己的能力和水平。

        懂源码的程序员才是真正的程序员。要想原生手写WebServer就需要从深入学习HTTP协议开始。

1.2.2 HTTP协议

        HTTP(Hypertext Transfer Protocol)协议是一种应用层协议,用于在Web浏览器和Web服务器之间传输数据。HTTP协议是Web的基础技术之一,它定义了客户端(如Web浏览器)和服务器之间的通信规则,使得Web可以实现信息的交互和共享。

        HTTP协议的设计是为了解决在Web上传输数据的问题。在早期的Web中,各种应用程序使用不同的通信协议,导致Web上的信息交流困难,信息共享也受到了限制。HTTP协议的出现解决了这些问题,使得Web的发展更加迅速和广泛。

        HTTP协议的优势如下:

  • 简单易用:HTTP协议的设计非常简单,易于理解和实现。这使得开发人员可以更快地开发Web应用程序,并且更容易调试和维护。
  • 可扩展性:HTTP协议的设计具有良好的可扩展性。这意味着可以通过添加新的功能和特性来改进HTTP协议,从而满足不断变化的需求。
  • 无状态:HTTP协议是无状态协议,它不保存任何关于请求或响应的状态信息。这使得Web服务器可以处理大量的请求,并提高了Web应用程序的可伸缩性。
  • 可靠性:HTTP协议的设计非常可靠,可以确保数据在客户端和服务器之间的安全传输。HTTP协议还支持数据压缩、数据加密等技术,提高了数据传输的效率和安全性。

        HTTP协议是一种简单易用、可扩展、无状态和可靠的应用层协议,它使得Web应用程序可以高效地传输和共享数据,从而推动了Web的发展和应用。

1.2.3 HTTP协议工作原理

        HTTP协议是一个客户端-服务器协议,它的工作原理可以分为以下几个步骤:

        1、建立连接:客户端向服务器发送一个连接请求,请求连接到服务器。客户端可以通过TCP/IP协议或TLS/SSL协议建立连接。

        2、发送请求:客户端向服务器发送HTTP请求,请求包括请求方法、请求头、请求体等信息。常见的请求方法包括GET、POST、PUT、DELETE等。

        3、处理请求:服务器接收到HTTP请求后,根据请求的方法和URL等信息进行处理。服务器可以返回HTTP响应,包括响应状态码、响应头和响应体等信息。

        4、发送响应:服务器向客户端发送HTTP响应,响应包括响应状态码、响应头、响应体等信息。

        5、关闭连接:一旦HTTP响应发送完毕,服务器和客户端都可以选择关闭连接。关闭连接可以释放网络资源,提高性能和安全性。

        在HTTP协议中,客户端和服务器之间的通信是通过HTTP报文进行的。HTTP报文分为请求报文和响应报文,分别用于客户端向服务器发送请求和服务器向客户端发送响应。HTTP报文包括起始行、头部字段和消息体等部分,它们用于传输数据和控制信息。

        总之,HTTP协议是一个客户端-服务器协议,它通过HTTP报文来传输数据和控制信息。HTTP协议的工作原理是建立连接、发送请求、处理请求、发送响应和关闭连接等步骤。

1.2.4 WebServer的处理步骤

        Java WebServer的大致处理步骤如下:

        1、创建一个ServerSocket对象:ServerSocket对象用于监听指定的端口,并接受客户端的请求。

        2、等待客户端连接:通过调用ServerSocket的accept()方法,等待客户端的连接请求。当有客户端连接时,accept()方法返回一个Socket对象,用于和客户端进行通信。

        3、解析HTTP请求:从Socket对象中读取客户端的请求数据,并将其解析为HTTP请求。HTTP请求由请求行、请求头和请求体组成。

        4、处理HTTP请求:根据HTTP请求中的方法、路径和参数等信息,处理客户端的请求,并生成HTTP响应。HTTP响应由状态行、响应头和响应体组成。

        5、发送HTTP响应:将HTTP响应发送回客户端,并关闭Socket连接。

        实现一个WebServer涉及到很多细节,例如解析HTTP请求、处理GET和POST请求、生成HTTP响应等。

1.3 接收HTTP请求

1.3.1 HTTP请求结构

        HTTP请求报文由三个部分组成:请求行、请求头和请求体。查看一个简单的HTTP GET请求的示例:

GET /hello.txt HTTP/1.1

Host: example.com

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

1、请求行

        请求行是HTTP请求报文的第一行,包括HTTP方法、请求URI和HTTP版本。请求行的格式如下:

METHOD URI HTTP_VERSION

        其中:METHOD为HTTP方法,通常为GET、POST、PUT、DELETE等;URI为请求资源的路径,可以包含查询参数;HTTP_VERSION为HTTP协议版本,通常为HTTP/1.1或HTTP/2。

        上个示例中,请求行包含了HTTP方法GET、请求路径/hello.txt和HTTP版本号HTTP/1.1。

2、请求头

        请求头紧随请求行之后,以一或多个以冒号分隔的键值对的形式提供附加信息。每个键值对为一行,键和值之间用冒号和空格分隔。请求头包含了客户端发送请求时的各种信息,如Accept、User-Agent、Host等。

        上个示例中,请求头包含了Host、User-Agent和Accept三个键值对。Host指定了服务器的域名或IP地址,User-Agent指定了浏览器的类型和版本,Accept指定了浏览器能够接受的响应格式。

3、请求体

        请求体是HTTP请求报文的可选部分,通常在使用POST或PUT方法提交表单数据时出现。请求体包含了客户端发送到服务器的实际数据,如表单字段、文件内容等。

        上个示例中,GET请求不包含请求消息体,以空行作为结束标识。

        下面是一个HTTP POST请求报文的示例:

POST /login HTTP/1.1

Host: example.com

Content-Type: application/x-www-form-urlencoded

Content-Length: 25

username=john&password=doe

        这个示例中,请求行为POST /login HTTP/1.1,表示使用POST方法向/login路径提交请求。请求头包括了Host、Content-Type和Content-Length三个键值对。请求体为username=john&password=doe,表示提交了用户名和密码两个表单字段的值。注意请求头和请求体之间有一个空行。其中Content-Length的长度25就是请求体中数据“username=john&password=doe”的字节数量。

        关于编码:请求行和请求头都是 ISO8859-1编码,不能直接使用中文,中文需要进行编码处理。

        HTTP协议的详细内容可以参考HTTP协议官方文档 RFC2616标准:RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1。

1.3.2 接收HTTP请求

        使用Java编程实现接收浏览器的HTTP请求,可以使用Java的Socket和ServerSocket类来实现一个简单的HTTP服务器。具体步骤如下:

        1、创建ServerSocket对象,并指定监听的端口号8088。

ServerSocket serverSocket = new ServerSocket(8088);

        2、使用accept()方法等待客户端的连接请求,并获取客户端的Socket对象。

Socket clientSocket = serverSocket.accept();

        3、从客户端Socket对象中获取输入流,先尝试一个简单方式读取HTTP请求报文(请求消息),读取代码示意如下:

InputStream in = clientSocket.getInputStream();
int b;
while ((b=in.read())!=-1){
    	System.out.print((char) b);
}
in.close();

1.3.3 接收HTTP请求实现

        编写服务端代码,实现接收HTTP请求,完整案例代码如下

public class ServerBootApplication {
    private ServerSocket serverSocket;

    public void start(){
        try {
            //创建ServerSocket对象,并指定监听的端口号8088。
            serverSocket = new ServerSocket(8088);
            //使用accept()方法等待客户端的连接请求,并获取客户端的Socket对象
            Socket clientSocket = serverSocket.accept();
            //从客户端Socket对象中获取输入流,读取HTTP请求报文(请求消息)。
            InputStream in = clientSocket.getInputStream();
            int b;
            while ((b=in.read())!=-1){
                System.out.println((char) b);
            }
            in.close();
            //关闭客户端连接
            clientSocket.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        //创建ServerBoot对象
        ServerBootApplication1 application = new ServerBootApplication1();
        //启动服务器
        application.start();
    }
}

        打开浏览器向 http://localhost:8088 发起请求,在开发工具控制台上输出如下信息:

        这些信息是一个浏览器发送的一个HTTP GET请求,不同浏览器信息略有区别。

        这个案例存在问题,客户浏览器会一直卡住“转圈圈”,原因是浏览器没有主动断开网络,只有断开网络时候,服务器端才能收到“-1”程序才能继续执行,否则就会在in.read()位置进行阻塞等待,客户端效果就是“转圈圈”。

        如何解决这个问题呢?要分析一下HTTP GET请求消息结构:

        GET请求消息每个行结束符号为“\r\n”,最后发送了空行“\r\n”为结束,我们改进程行读取到空行就结束读取,让循环结束,代码改进如下:

//从客户端Socket对象中获取输入流,读取HTTP请求报文(请求消息)。
InputStream in = clientSocket.getInputStream();
StringBuilder builder= new StringBuilder();
//   前一个字符  当前字符
char previous = 0, current = 0;
int b;
while ((b=in.read())!=-1){
    //将读取的字节存储到当前字符, 由于请求头采用了ISO8859-1编码,
    // 所以可以讲字节直接转化为字符类型
    current = (char) b;
    //如果发现了 前一个字符是 \r 当前字符是 \n 就读取到了行末尾
    if (previous == '\r' && current == '\n'){
        //如果这一行是空行就结束处理了
        if (builder.toString().isEmpty()){
            break;
        }
        //输出这一行数据当前一行数据并且清空builder,为下次缓存数据做准备
        System.out.println(builder);
        builder.delete(0, builder.length());
    }else if (current != '\r' && current != '\n'){
        //当前的不是 \r \n 就是一行中的字符
        builder.append(current);
    }
    //最后将当前的字符作为下次的前一个字符
    previous = current;
}
in.close();

        这段代码从客户端Socket对象的输入流中读取HTTP请求报文(请求消息)。该代码使用一个StringBuilder对象来存储读取到的数据,并使用一个while循环遍历输入流中的字节。

        在while循环中,代码将当前字节转换为字符类型并存储到变量current中,同时检查前一个字符是否是回车符(\r)并且当前字符是否是换行符(\n),如果是,就表示读取到了一行的末尾,将该行数据输出并清空StringBuilder对象。

        如果当前字符不是回车符或换行符,那么就是一行中的字符,将该字符添加到StringBuilder对象中。

        在处理完一行数据后,将当前字符作为下次循环的前一个字符;最后,关闭输入流。

        需要注意的是,请求报文采用了ISO8859-1编码,因此可以将字节直接转换为字符类型。

        案例的完整代码如下:

public class ServerBootApplication {
    private ServerSocket serverSocket;

    public void start(){
        try {
            //创建ServerSocket对象,并指定监听的端口号8088。
            serverSocket = new ServerSocket(8088);
            //使用accept()方法等待客户端的连接请求,并获取客户端的Socket对象
            Socket clientSocket = serverSocket.accept();
            //从客户端Socket对象中获取输入流,读取HTTP请求报文(请求消息)。
            InputStream in = clientSocket.getInputStream();
            StringBuilder builder= new StringBuilder();
            //   前一个字符  当前字符
            char previous = 0, current = 0;
            int b;
            while ((b=in.read())!=-1){
                //将读取的字节存储到当前字符, 由于请求头采用了ISO8859-1编码,
                // 所以可以讲字节直接转化为字符类型
                current = (char) b;
                //如果发现了 前一个字符是 \r 当前字符是 \n 就读取到了行末尾
                if (previous == '\r' && current == '\n'){
                    //如果这一行是空行就结束处理了
                    if (builder.toString().isEmpty()){
                        break;
                    }
                    //输出这一行数据当前一行数据并且清空builder,为下次缓存数据做准备
                    System.out.println(builder);
                    builder.delete(0, builder.length());
                }else if (current != '\r' && current != '\n'){
                    //当前的不是 \r \n 就是一行中的字符
                    builder.append(current);
                }
                //最后将当前的字符作为下次的前一个字符
                previous = current;
            }
            in.close();

            //关闭客户端连接
            clientSocket.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        //创建ServerBoot对象
        ServerBootApplication application = new ServerBootApplication();
        //启动服务器
        application.start();
    }
}

        服务器端可以正常显示浏览器的请求信息,并且服务端程序可以正常结束:

1.4 发送HTTP响应

1.4.1 HTTP响应

        在上一节案例中虽然服务器端结束了,但是客户端端得到了一个不正常的结果:

        其原因是:服务器没有向浏览器发送任何响应消息,浏览器没有收到任何信息。解决办法就是在服务端程序,向浏览器发送响应消息。

1.4.2 响应消息结构

        要能正确发送响应消息就必须了解完整的响应消息结构。HTTP响应消息由三部分组成:状态行、响应头和响应正文。

1、状态行

        状态行由HTTP协议版本、状态码和状态描述组成,通常格式如下:

HTTP/1.1 200 OK

        其中,HTTP/1.1表示HTTP协议的版本,200表示状态码,OK是状态描述。

Content-Type: text/html

Content-Length: 1234

Date: Fri, 25 Feb 2023 10:00:00 GMT

        响应正文是服务器返回的实际数据,可以是HTML网页、图片、文本等等。响应正文的格式和内容取决于服务器返回的数据类型和内容。

        完整的HTTP响应消息结构如下:

HTTP/1.1 200 OK

Content-Type: text/html; charset=utf-8

Content-Length: 1234

Date: Fri, 25 Feb 2023 10:00:00 GMT

<html>

<head>

<title>Example</title>

</head>

<body>

<p>This is an example.</p>

</body>

</html>

        其中响应头Content-Type: text/html; charset=utf-8 用于说明,响应正文中的内容类型,这是text/html表示,响应正文中是一个html网页,charset=utf-8表示响应正文中的网页采用UTF-8编码。

        响应头Content-Length: 1234,用于说明响应正文中内容长度,单位是字节数量。

        需要注意的是,HTTP响应消息中的每个部分都使用特定的分隔符进行分割。状态行和响应头之间使用一个空行进行分割,响应头和响应正文之间也使用一个空行进行分割。服务器端需要按照HTTP协议规定的格式构造响应消息,客户端收到响应消息后也需要按照HTTP协议规定的方式解析响应消息。

1.4.3 向浏览器发送HTTP响应

        在Java中向浏览器发送HTTP响应需要借助Java中的Socket和OutputStream等类。以下是一个简单的Java程序示例,可以向浏览器发送一段HTML内容的HTTP响应:

OutputStream out = clientSocket.getOutputStream();
//一个简单的网页内容
String html = "<html>\n" +
        "<head>\n" +
        "<title>Example</title>\n" +
        "</head>\n" +
        "<body>\n" +
        "<p>Hello World!</p>\n" +
        "</body>\n" +
        "</html>";
byte[] body = html.getBytes(StandardCharsets.UTF_8);
out.write("HTTP/1.1 200 OK".getBytes(StandardCharsets.ISO_8859_1));
out.write('\r');
out.write('\n');
out.write("Content-Type: text/html; charset=utf-8"
	.getBytes(StandardCharsets.ISO_8859_1));
out.write('\r');
out.write('\n');
out.write(("Content-Length: "+body.length)
	.getBytes(StandardCharsets.ISO_8859_1));
out.write('\r');
out.write('\n');
out.write('\r'); //空行
out.write('\n');
out.write(body);
//关闭客户端连接
out.close();

        这段代码用于向客户端发送HTTP响应。具体来说,它先构造了一个简单的HTML网页,然后将HTML内容转换成UTF-8编码的字节数组,将HTTP响应头和响应体分别写入到客户端的输出流中。

        HTTP响应的第一行为状态行,这里写入了“HTTP/1.1 200 OK”,表示HTTP版本为1.1,状态码为200,状态码200表示请求成功。接着写入了响应头信息,包括“Content-Type”表示响应体类型为HTML文本,“charset=utf-8”表示响应体采用的字符集为UTF-8,“Content-Length”表示响应体长度为body的字节数组长度。之后写入一个空行,表示响应头和响应体的分隔符,最后将响应体内容写入到输出流中。

        最后,关闭客户端的输出流,表示该响应已经发送完毕,可以断开与客户端的连接。

        完整案例:

package com.obj.boot;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

public class ServerBootApplication {
    private ServerSocket serverSocket;

    public void start(){
        try {
            //创建ServerSocket对象,并指定监听的端口号8088。
            serverSocket = new ServerSocket(8088);
            //使用accept()方法等待客户端的连接请求,并获取客户端的Socket对象
            Socket clientSocket = serverSocket.accept();
            //从客户端Socket对象中获取输入流,读取HTTP请求报文(请求消息)。
            InputStream in = clientSocket.getInputStream();
            StringBuilder builder= new StringBuilder();
            //   前一个字符  当前字符
            char previous = 0, current = 0;
            int b;
            while ((b=in.read())!=-1){
                //将读取的字节存储到当前字符, 由于请求头采用了ISO8859-1编码,
                // 所以可以讲字节直接转化为字符类型
                current = (char) b;
                //如果发现了 前一个字符是 \r 当前字符是 \n 就读取到了行末尾
                if (previous == '\r' && current == '\n'){
                    //如果这一行是空行就结束处理了
                    if (builder.toString().isEmpty()){
                        break;
                    }
                    //输出这一行数据当前一行数据并且清空builder,为下次缓存数据做准备
                    System.out.println(builder);
                    builder.delete(0, builder.length());
                }else if (current != '\r' && current != '\n'){
                    //当前的不是 \r \n 就是一行中的字符
                    builder.append(current);
                }
                //最后将当前的字符作为下次的前一个字符
                previous = current;
            }

            OutputStream out = clientSocket.getOutputStream();
            //一个简单的网页内容
            String html = "<html>\n" +
                    "<head>\n" +
                    "<title>Example</title>\n" +
                    "</head>\n" +
                    "<body>\n" +
                    "<p>Hello World!</p>\n" +
                    "</body>\n" +
                    "</html>";
            byte[] body = html.getBytes(StandardCharsets.UTF_8);
            out.write("HTTP/1.1 200 OK".getBytes(StandardCharsets.ISO_8859_1));
            out.write('\r');
            out.write('\n');
            out.write("Content-Type: text/html; charset=utf-8".getBytes(StandardCharsets.ISO_8859_1));
            out.write('\r');
            out.write('\n');
            out.write(("Content-Length: "+body.length).getBytes(StandardCharsets.ISO_8859_1));
            out.write('\r');
            out.write('\n');
            out.write('\r'); //空行
            out.write('\n');
            out.write(body);
            //关闭客户端连接
            out.close();
            in.close();
            clientSocket.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        //创建ServerBoot对象
        ServerBootApplication application = new ServerBootApplication();
        //启动服务器
        application.start();
    }
}

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

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

相关文章

kettle将excel表数据导入到oracle表中

上一篇已经介绍过kettle8.2的安装。 之前一直使用的sqlldr导入外部表&#xff0c;导入比较耗时&#xff0c;这次想使用一下kettle试试。 1.新建转换 2.新建输入 3.新建输出 4.转换新建完成 5.配置输入 加载表格文件 配置工作表 加载字段 6.配置输出 测试数据库连接 这…

指标完成情况对比查询sql

指标完成情况对比查询sql 1. 需求 2. SQL select--部门dept.name as bm,--年度指标任务-新签&#xff08;万元&#xff09;ndzbwh.nxqndzbrw as nxqndzbrw,--年度指标任务-收入&#xff08;万元&#xff09;ndzbwh.nsrndzbrw as nsrndzbrw,--年度指标任务-回款&#xff08;万…

大数据中的项目数据采集

Datax介绍 官网&#xff1a; DataX/introduction.md at master alibaba/DataX GitHub DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。 DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS…

C、Minimizing the Sum(线性dp)

思路&#xff1a; 用dp[i][j] 来表示前i个数操作了j次的最小和&#xff0c;然后对于每个a[i]&#xff0c;我们分别枚举i前面操作了x次以及后面操作了j次&#xff0c;对于每次操作&#xff0c;都是将一段区间全换位区间最小值. 代码&#xff1a; void solve(){int n, k;cin &…

基于SpringBoot+Vue大学生兼职管理系统的设计与实现

目录 一、前言介绍 二、功能需求 三、功能结构设计 四、管理员功能实现 招聘单位管理 用户管理 论坛管理 公告信息管理 五、招聘单位功能实现 职位招聘管理 职位留言管理 简历投递管理 六、用户功能实现 在线论坛 职位招聘信息 简历投递 简历 七、部分核心代码 …

代码随想录算法训练营第二十六天||39. 组合总和、40.组合总和II、131.分割回文串

文章目录 一、39. 组合总和 思路 二、40.组合总和II 思路 三、131.分割回文串 思路 一、39. 组合总和 给定一个无重复元素的数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取…

活性炭复合纳米纤维膜

活性炭复合纳米纤维膜是一种结合了活性炭和纳米纤维技术的新型复合材料。这种材料通常通过特定的制备工艺&#xff0c;如静电纺丝技术&#xff0c;将活性炭纳米纤维与其他材料&#xff08;如TiO2、聚合物等&#xff09;结合在一起&#xff0c;形成具有良好结构和功能的薄膜。 活…

【SpringBoot】数据脱敏

文章目录 什么是数据脱敏JsonSerialize自定义Jackson注解定制脱敏策略定制JSON序列化实现脱敏工具类 定义Person类&#xff0c;对其数据脱敏模拟接口测试总结 什么是数据脱敏 数据脱敏&#xff0c;也称为数据的去隐私化或数据变形&#xff0c;是一种技术手段&#xff0c;用于对…

【酱浦菌-爬虫技术细节】解决学术堂爬虫翻页(下一页)问题

首先我们通过css选择器获取页码信息&#xff0c;这里的css选择器&#xff0c;选择的是含有a标签的所有li标签&#xff0c;代码如下&#xff1a; li html_web.css(div.pd_c_xslb_left_fenye ul li>a) for li in li:li_url li.css(a::attr(href)).get()li_num li.css(a::t…

排序算法:插入、希尔、选择、推排、冒泡、快速、归并排序

排序算法 目录 前言 一、排序的概念 1.1排序的概念 1.2 常见的排序算法 二、常见排序算法的实现 2.1 插入排序 2.2 希尔排序 2.3 选择排序 2.4 堆排序 2.5 冒泡排序 2.6 快速排序 2.6.1 hoare版本 2.6.2 前后指针版本 2.6.3 非递归版本 2.7 归并排序 归并排序 2.8 计数排序 三、…

【c++】优先级队列与仿函数:C++编程的强大组合

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章我们来讲解优先级队列priority_queue 目录 1.priority_queue的介绍和使用函数使用仿函数的使用与介绍greater和less 2.priority_queue的模拟实现基本框架…

C++ dll 分别 给c# c++使用

一、C# 自己写2D 3D算法库都要给别人用&#xff0c;所以好几年没有用过c# 了&#xff0c;调用一些 1、建立c 项目 .h 文件 #pragma once #ifdef __DLLEXPORT #define __DLL_EXP _declspec(dllexport) #else #define __DLL_EXP _declspec(dllimport) #endif extern "…

第1篇:创建Platform Designer系统

Q&#xff1a;本期我们开始使用Platform Designer工具创建带IP核的FPGA自定义硬件系统。 A&#xff1a;Platform Designer是集成在Quartus软件里的系统设计工具&#xff0c;名称随着Quartus的不断更新曾命名为SOPC Builder和Qsys。 使用Platform Designer可以添加Quartus已有自…

Python 与 TensorFlow2 生成式 AI(五)

原文&#xff1a;zh.annas-archive.org/md5/d06d282ea0d9c23c57f0ce31225acf76 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第十二章&#xff1a;用生成式人工智能玩视频游戏&#xff1a;GAIL 在之前的章节中&#xff0c;我们已经看到如何使用生成式人工智能来生成…

Stable Diffusion基础:ControlNet之线稿成图

今天继续给大家分享Stable Diffusiion的基础能力&#xff1a;ControlNet之线稿成图。 所谓线稿就是由一条条的线段组成的图形&#xff0c;主要用于绘画和设计领域的打底稿、表达构想和预见最终效果。 所谓线稿成图就是利用 Stable Diffusion ControlNet 的能力&#xff0c;依…

GIT入门到实战

文章目录 版本控制常见的版本控制工具版本控制分类Git与SVN的主要区别 Git基本理论&#xff08;重要&#xff09;三个区域工作流程 GIT文件操作文件的四种状态查看文件状态忽略文件 GIT 常见问题 版本控制 版本控制&#xff08;Revision control&#xff09;是一种在开发的过程…

【安卓13】谷歌桌面Launcher3屏蔽全部应用里面的部分app

1、需求 我们在做谷歌桌面时&#xff0c;移植了一些我们自己的应用&#xff0c;但是有些应用是服务型的app&#xff0c;不需要显示在主页&#xff0c;要隐藏掉 2、解决方案 方法1&#xff1a; 解决办法很简单&#xff0c;阅读源码发现&#xff0c;谷歌桌面添加全部应用的源…

Linux编辑器调试器 gcc/g++ gdb 编译过程及使用讲解

这恋爱呀 我有两不谈 第一异性不谈 因为我们性别不一样 我知道的她不知道相处起来太累 第二同性不谈 因为我们性别一样 我知道的他也知道相处起来太无聊了 –❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀-正文开始-❀–❀–❀–❀–❀–❀–…

Oracle程序常驻程序内存优化【数据库实例优化系列二】

Oracle系统参数调整【数据库实例优化系列一】-CSDN博客 Oracle数据库中有一个软件包 dbms_shared_pool,它可以keep和unkeep,将用户经常使用的程序,比如存储过程、函数、序列、触发器、游标以及java source等数据库对象,长期保存在这一块区域。这些程序可以常驻这个区域(s…

若依前后端部署系统--详细附图

一、后端部署 1、在ruoyi项目的Maven中的生命周期下双击package.bat打包Web工程&#xff0c;生成jar包文件。 提示打包成功 2、多模块版本会生成在ruoyi/ruoyi-admin模块下target文件夹,我们打开目录ruoyi-admin/taget&#xff0c;打开cmd&#xff0c;运行java -jar jar包名称…