第2章 WebServer进阶

2.1 使用多线程处理多用户请求

2.1.1 多线程Socket通信

        在上一章的案例中,服务端显然只能处理一次浏览器请求,请求一次浏览器端就结束程序。如何解决这个问题呢?可以采用多线程Socket通信技术,解决多用户并发请求。

        在多线程Socket通信中,服务端会启动一个主线程用于监听客户端的连接请求,并为每个客户端连接请求创建一个新的子线程进行处理。这样可以保证服务端能够同时处理多个客户端的请求,提高系统的并发性能和稳定性。

        具体流程如下:

        1. 服务端启动主线程监听客户端的连接请求;

        2. 当有新的客户端连接请求时,主线程创建一个新的子线程来处理该客户端请求;

        3. 子线程接收客户端的请求信息,并根据请求内容进行相应的业务处理;

        4. 子线程将处理结果封装成响应报文发送给客户端;

        5. 子线程关闭连接,结束线程。

        这样,服务端就可以同时处理多个客户端请求,实现了高并发处理能力。同时,使用多线程编程也能提高代码的可维护性和可扩展性,减少代码耦合度。

2.1.2 使用多线程处理HTTP通信

        首先定义ClientHandler,作为线程处理HTTP请求和发生HTTP响应:

public class ClientHandler implements Runnable {

    private Socket socket;

    public ClientHandler(Socket clientSocket){
        socket = clientSocket;
    }
    @Override
    public void run() {
        try {
            //从客户端Socket对象中获取输入流,读取HTTP请求报文(请求消息)。
            InputStream in = socket.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 = socket.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();
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

        然后重构start()方法:

public class ServerBootApplication {
    private ServerSocket serverSocket;

    public void start(){
        try {
            //创建ServerSocket对象,并指定监听的端口号8088。
            serverSocket = new ServerSocket(8088);
            while (true) {
                //使用accept()方法等待客户端的连接请求,并获取客户端的Socket对象
                Socket clientSocket = serverSocket.accept();
                ClientHandler clientHandler = new ClientHandler(clientSocket);
                Thread thread = new Thread(clientHandler);
                thread.start();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        //创建ServerBoot对象
        ServerBootApplication application = new ServerBootApplication();
        //启动服务器
        application.start();
    }
}

        该代码是一个典型的多线程Socket通信的服务器端代码结构。在主线程中,创建了一个ServerSocket对象,并指定要监听的端口号8088。接着,通过一个while循环不断地使用accept()方法等待客户端的连接请求,一旦接收到请求,就会创建一个新的ClientHandler对象,将客户端的Socket对象传递给它,然后将ClientHandler对象封装成一个新的线程并启动,用于处理客户端的请求。在这个过程中,主线程一直保持监听状态,等待下一个客户端连接。

        为了处理多个客户端的并发请求,每个ClientHandler对象都运行在一个单独的线程中,这使得服务器可以同时处理多个客户端的请求,提高了系统的并发处理能力。

        经过上述重构我们的WebServer就可以处理多用户的并发请求了。

2.1.3 关于favicon.ico

        实现了多线程Web请求处理功能以后,控制台上出现了如下请求信息:

        由图可以看出:显然请求了favicon.ico文件。

        favicon.ico是一个网站上常见的文件,它是网站的图标文件,通常会显示在网站的标签页和书签上。当用户访问一个网站时,浏览器会自动请求这个文件,以便在标签页和书签上显示网站的图标。因此,在服务器的请求日志中,我们会看到很多关于 favicon.ico 的请求记录。这些请求记录是非常正常的现象,不必过于关注。

        理解这些请求信息有助于我们对网站访问进行监控和分析。通常情况下,这些请求不会对服务器性能产生重大影响,因为 favicon.ico 文件通常是相对较小的图标文件,并且浏览器会进行缓存,减少了对服务器的重复请求。

        因此,不必担心看到这些 favicon.ico 请求记录,除非你注意到在短时间内出现异常的请求量,这可能是有人在恶意攻击或者其他异常情况,需要进一步分析和处理。否则,这些请求是正常的,不需要特别处理。

2.2 解析请求行

2.2.1 解析请求行

        HTTP请求行是HTTP请求报文的第一行,包括HTTP方法、请求URL和HTTP协议版本号。例如:

GET /index.html HTTP/1.1

        其中,GET表示HTTP请求方法,/index.html表示请求的资源URL,HTTP/1.1表示使用的HTTP协议版本号。

        解析HTTP请求行可以获取请求方法、请求URL和协议版本等信息,这些信息对于服务器来说非常重要,可以根据这些信息对请求进行处理和响应。例如,根据请求URL可以确定请求的资源类型和位置,从而进行处理和响应;根据请求方法可以确定请求的类型(如GET、POST、PUT、DELETE等),从而采取相应的处理方式。因此,解析HTTP请求行是Web服务器处理HTTP请求的重要步骤之一。

        前述多线程的WebServer案例虽然能过处理用户请求,但是用户发起任何请求都会得到相同的响应结果,比如发送:http://localhost:8088/ 、http://localhost:8088/index.html和 http://localhost:8088/demo.html,都得到如下结果:

        这个结果显然不理想:不可能任何请求都返回相同的响应结果。正确情况应该是:请求index.html就显示index.html文件的内容,请求demo.index文件就显示demo.html的内容。

2.2.2 显示正确的请求内容

        如何解决这个问题呢?解决的办法就是将请求行进行解析,找出客户端发起的请求资源路径,根据请求资源的路径找到响应的资源,发送响应到客户端浏览器。这样就可以在用户请求不同资源时候,响应不同的结果:

2.2.3 读取请求行

        请求行在请求消息的第一行,请求行以\r\n为结尾,可以使用算法在读取请求头之前读取第一行作为请求行,如下代码可以读取请求的第一行:

InputStream in = socket.getInputStream();
StringBuilder builder= new StringBuilder();
//   前一个字符  当前字符
char previous = 0, current = 0;
int b;
String requestLine = null;
//解析请求行
while ((b=in.read())!=-1){
    current = (char) b;
    if (previous == '\r' && current == '\n'){
        requestLine = builder.toString();
        builder.delete(0, builder.length());
        break;
    }else if (current != '\r' && current != '\n'){
        builder.append(current);
    }
    previous = current;
}
System.out.println(requestLine);

        显然这个算法和读取请求头的代码是重复的,所以可以尝试将代码重构抽取一个从输入流中读取一行的方法:

public String readLine() throws IOException{
    InputStream in = socket.getInputStream();
    StringBuilder builder= new StringBuilder();
    //   前一个字符  当前字符
    char previous = 0, current = 0;
    int b;
    //解析请求行
    while ((b=in.read())!=-1){
        current = (char) b;
        if (previous == '\r' && current == '\n'){
            //遇到行结束就结束读取
            break;
        }else if (current != '\r' && current != '\n'){
            builder.append(current);
        }
        previous = current;
    }
    return builder.toString();
}

        这段代码的作用是从Socket的输入流中读取一行数据并返回。它通过InputStream获取Socket的输入流,然后使用一个StringBuilder对象来存储读取的数据,最终返回读取的数据。

        具体实现逻辑如下:

        1. 创建一个InputStream对象in,并将其设置为socket的输入流。

        2. 创建一个StringBuilder对象builder,用于存储读取的数据。

        3. 定义两个字符变量previous和current,用于记录前一个字符和当前字符。

        4. 定义一个int类型变量b,用于记录从输入流中读取的字节。

        5. 使用while循环从输入流中读取字节,直到读取完一行数据。

        6. 将读取到的字节转换成字符类型,并赋值给变量current。

        7. 判断当前字符是否为行结束符("\r\n"),如果是则退出循环,否则将当前字符添加到builder中。

        8. 将当前字符赋值给previous,以备下次循环使用。

        9. 循环结束后,将builder转换成字符串并返回。

        重构后的ClientHandler代码就清爽许多:

public class ClientHandler implements Runnable {
    private Socket socket;
    public ClientHandler(Socket clientSocket){
        socket = clientSocket;
    }

    @Override
    public void run() {
        try {
            //从客户端Socket对象中获取输入流,读取HTTP请求报文(请求消息)。
            InputStream in = socket.getInputStream();
            //读取请求行
            String requestLine = readLine();
            System.out.println(requestLine);
            //读取请求头
            String requestHeader;
            //读取到空行就不在读取请求头了
            while (!(requestHeader = readLine()).isEmpty()){
                System.out.println(requestHeader);
            }

            OutputStream out = socket.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();
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 这段代码的作用是从Socket的输入流中读取一行数据并返回。
     * @return 从Socket的输入流中读取一行数据并返回
     * @throws IOException 出现网络IO错误
     */
    public String readLine() throws IOException{
        InputStream in = socket.getInputStream();
        StringBuilder builder= new StringBuilder();
        //   前一个字符  当前字符
        char previous = 0, current = 0;
        int b;
        //解析请求行
        while ((b=in.read())!=-1){
            current = (char) b;
            if (previous == '\r' && current == '\n'){
                //遇到行结束就结束读取
                break;
            }else if (current != '\r' && current != '\n'){
                builder.append(current);
            }
            previous = current;
        }
        return builder.toString();
    }
}

2.2.4 解析请求行

        上述代码实现了,读取请求行,读取后需要从请求行中解析其中的每个部分,然后可以根据请求行找到相应的本地文件资源,发送响应给浏览器,显示不同的资源内容。

        解析请求行:

String[] line = requestLine.split("\\s");
String method = line[0];
String uri = line[1];
String protocol = line[1];
System.out.println("method: "+method);
System.out.println("uri: " + uri);
System.out.println("protocol: " + protocol);

        这段代码用于解析HTTP请求报文中的请求行。首先,将请求行按照空白字符进行分割,得到一个包含请求方法、URI和协议版本三个字段的字符串数组。接着,将这三个字段分别存储到对应的变量中,并打印出来以供调试或其他用途。

        具体解释如下:

  • 使用String类的split()方法按照空白字符(包括空格、制表符和换行符)对请求行进行分割,得到一个包含请求方法、URI和协议版本三个字段的字符串数组line
  • 将line数组中的第一个元素存储到字符串变量method中,第二个元素存储到字符串变量uri中,第三个元素存储到字符串变量protocol中
  • 最后,使用System.out.println()方法打印出method、uri和protocol的值,方便调试和查看解析结果

        需要注意的是,该代码仅仅是对HTTP请求报文中的请求行进行了最基本的解析,仅适用于最简单的HTTP请求,对于复杂的HTTP请求报文,需要进行更加严谨和完整的解析。

        重新启动后服务端后,用浏览器发起 http://localhost:8088/index.html 请求,控制台信息包含如下信息:

        这个信息表示请求行解析成功。

2.3 响应静态资源

2.3.1 创建静态资源

        前述项目完成了解析请求行,从请求行中得到了请求资源的路径URI,为了能响应客户端需求,返回对应的资源,所以要建立静态文件夹存储静态资源:

        index.html 文件是一个html文件,格式类似于html,内容如下:

<!DOCTYPE html>
<html lang="cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>Hello World</h1>
</body>
</html>

        demo.html 文件内容如下:

<!DOCTYPE html>
<html lang="cn">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>Demo Page</h1>
</body>
</html>

        有了这了两个资源,就可以实现根据用户请求的uri,找到这两个资源,并且发送到响应到浏览器,实现根据用户请求响应不同文件的功能。

2.3.2 响应静态资源

        重构服务端代码,根据URI在resources/static文件夹中找到静态资源,并且将静态资源响应给客户端,原理为:

2.3.3 响应静态资源

        重构后的代码:

//发送响应
//根据找到静态资源
//类加载路径:target/classes
File root = new File(
        ClientHandler.class.getClassLoader().getResource(".").toURI()
);
//定位target/classes/static目录(SpringBoot中存放所有静态资源的目录)
File staticDir = new File(root,"static");
//定位target/classes/static目录中的文件
File file = new File(staticDir,uri);
//读取文件的全部内容
byte[] bytes = new byte[(int)file.length()];
FileInputStream fin = new FileInputStream(file);
fin.read(bytes);
fin.close();

OutputStream out = socket.getOutputStream();

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: "+bytes.length).getBytes(StandardCharsets.ISO_8859_1));
out.write('\r');
out.write('\n');
out.write('\r'); //空行
out.write('\n');
out.write(bytes);
//关闭客户端连接
out.close();
in.close();

        这段代码是一个简单的HTTP服务器响应客户端请求的部分,主要功能是根据请求的URI定位服务器上的静态资源,并将其发送给客户端。

        具体来说,代码首先根据当前类的类加载器获取类加载路径,然后根据该路径找到服务器上的静态资源所在目录。接着,根据URI定位静态资源文件,并读取该文件的全部内容到一个字节数组中。

        然后,代码使用Java的Socket API将HTTP响应发送给客户端。响应包括HTTP响应头和响应体两部分。响应头中包含HTTP协议版本、状态码和响应内容类型等信息,而响应体则包含实际的静态资源内容。发送响应的代码使用OutputStream将响应数据写入到客户端的Socket连接中,并在最后关闭客户端连接。

        完成的ClientHandler参考如下:

public class ClientHandler implements Runnable {

    private Socket socket;
    public ClientHandler(Socket clientSocket){
        socket = clientSocket;
    }

    @Override
    public void run() {
        try {
            //从客户端Socket对象中获取输入流,读取HTTP请求报文(请求消息)。
            InputStream in = socket.getInputStream();
            //读取请求行
            String requestLine = readLine();
            System.out.println(requestLine);
            //解析请求行
            String[] line = requestLine.split("\\s");
            String method = line[0];
            String uri = line[1];
            String protocol = line[1];
            System.out.println("method: "+method);
            System.out.println("uri: " + uri);
            System.out.println("protocol: " + protocol);

            //读取请求头
            String requestHeader;
            //读取到空行就不在读取请求头了
            while (!(requestHeader = readLine()).isEmpty()){
                System.out.println(requestHeader);
            }

            //发送响应
            //根据找到静态资源
            //类加载路径:target/classes
            File root = new File(
                    ClientHandler.class.getClassLoader().getResource(".").toURI()
            );
            //定位target/classes/static目录(SpringBoot中存放所有静态资源的目录)
            File staticDir = new File(root,"static");
            //定位target/classes/static目录中的文件
            File file = new File(staticDir,uri);
            //读取文件的全部内容
            byte[] bytes = new byte[(int)file.length()];
            FileInputStream fin = new FileInputStream(file);
            fin.read(bytes);
            fin.close();

            OutputStream out = socket.getOutputStream();

            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: "+bytes.length).getBytes(StandardCharsets.ISO_8859_1));
            out.write('\r');
            out.write('\n');
            out.write('\r'); //空行
            out.write('\n');
            out.write(bytes);
            //关闭客户端连接
            out.close();
            in.close();
        }catch (IOException | URISyntaxException e){
            e.printStackTrace();
        }finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 这段代码的作用是从Socket的输入流中读取一行数据并返回。它通过InputStream获取Socket的输入流,
     * 然后使用一个StringBuilder对象来存储读取的数据,最终返回读取的数据。
     * @return 从Socket的输入流中读取一行数据并返回
     * @throws IOException 出现网络IO错误
     */
    public String readLine() throws IOException{
        InputStream in = socket.getInputStream();
        StringBuilder builder= new StringBuilder();
        //   前一个字符  当前字符
        char previous = 0, current = 0;
        int b;
        //解析请求行
        while ((b=in.read())!=-1){
            current = (char) b;
            if (previous == '\r' && current == '\n'){
                //遇到行结束就结束读取
                break;
            }else if (current != '\r' && current != '\n'){
                builder.append(current);
            }
            previous = current;
        }
        return builder.toString();
    }
}

2.3.4 HTTP协议补充

        HTTP(Hypertext Transfer Protocol)是一种用于在计算机网络上传输超文本数据的协议。它是Web应用程序中最基本的通信协议,用于在客户端(例如浏览器)和服务器之间传输数据。

        HTTP的主要特点包括:

  • 状态无关:HTTP是一种无状态协议,即服务器不会记录客户端之前的请求信息。每个HTTP请求都是独立的,服务器不会保留客户端的状态信息,这样可以降低服务器的负担,也使得HTTP协议具有良好的扩展性。
  • 请求-响应模型:HTTP是基于请求-响应模型的协议。客户端发送HTTP请求到服务器,然后服务器返回HTTP响应。请求包括请求方法(GET、POST等)、请求头、请求体等信息,响应包括状态码、响应头、响应体等信息。
  • 可靠性:HTTP在传输过程中使用TCP协议作为传输层协议,因此具有可靠性。TCP协议会确保数据的正确传输和接收,如果数据丢失或损坏,TCP会自动重传。
  • 简单灵活:HTTP协议采用文本形式传输数据,易于阅读和调试。同时,HTTP协议也非常灵活,可以传输不同类型的数据,支持多种编码和内容类型。
  • 支持缓存:HTTP协议支持缓存机制,通过在响应中添加缓存相关的头信息,可以让浏览器在下次请求相同资源时直接从缓存中获取,提高性能和加载速度。

        HTTP协议的工作方式是客户端向服务器发送请求,服务器根据请求进行处理并返回响应。客户端和服务器通过URL(统一资源定位符)来定位资源,URL由协议类型(例如http)、服务器地址和资源路径组成。

        HTTP协议是Web开发中非常重要的基础,它使得浏览器能够请求并获取Web页面、图片、视频、文件等资源,并实现了Web应用程序的交互性。同时,HTTP也不断发展,出现了新的版本,例如HTTP/1.1和HTTP/2,以满足不断增长的Web应用需求。

2.3.5 幂等性

        在计算机科学和网络编程中,幂等性(Idempotence)是指对同一个操作进行一次或多次的操作,产生的结果是相同的。换句话说,无论对一个操作进行多少次重复,其结果都是一致的。

        幂等性在计算机系统设计和网络通信中具有重要意义,特别是在处理故障、网络延迟或重试等情况下。幂等性操作保证了系统对相同请求的重复处理不会导致副作用或错误结果。

        举个简单的例子,假设有一个用于更新用户信息的API接口。如果这个API是幂等的,那么当多个请求同时更新同一个用户信息时,无论请求执行多少次,最终用户的信息都只会更新一次,而不会因为重复的请求导致用户信息被错误地更新多次。

        在实际应用中,一些常见的幂等性操作包括:

  • GET请求:GET请求是幂等的,因为对于同一个URL的GET请求,无论请求执行多少次,都只会返回相同的响应结果,不会对服务器产生任何副作用。
  • PUT请求:PUT请求通常用于更新资源,在幂等性的设计下,对于同一个URL的PUT请求,重复执行对资源的更新操作将得到相同的结果。
  • DELETE请求:DELETE请求通常用于删除资源,在幂等性的设计下,对于同一个URL的DELETE请求,重复执行对资源的删除操作将得到相同的结果。

        幂等性的接口设计:在设计API接口时,如果接口的操作具有幂等性,可以增强系统的稳定性和可靠性。通过一些设计措施,比如生成唯一的请求标识,对于相同的请求标识只处理一次,就能实现幂等性。

        总的来说,幂等性操作对于构建健壮和可靠的系统非常重要,可以避免重复操作导致的不一致性和错误结果。在网络通信中,幂等性的操作可以增加系统的容错性,确保请求的可靠传输和正确处理。

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

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

相关文章

十四、网络编程

目录 一、二、网络通讯要素三、IP和端口号四、网络协议1、网络通信协议2、TCP/IP协议簇1&#xff09;TCP协议2&#xff09;UDP 3、Socket 五、TCP网络编程1、基于Socket的TCP编程1&#xff09;客户端创建socket对象2&#xff09; 服务器端建立 ServerSocket对象 2、UDP网络通信…

理想二极管LM74700QDBVRQ1

LM74700QDBVRQ1 防反接专用芯片 器件手册 应用参考&#xff08;下图是另外一个理想二极管应用电路图&#xff09; 这两款芯片的区别主要是工作电压范围不同&#xff08;实际应用是&#xff09; 电源远端电压补偿-CSDN博客https://blog.csdn.net/anlog/article/details/1338627…

顶顶顶顶顶顶顶顶顶顶顶顶

欢迎关注博主 Mindtechnist 或加入【智能科技社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和技术。关…

SVM单类异常值检测

SVM是一种广泛使用的分类器&#xff0c;通常用于二分类或多分类问题。然而&#xff0c;在异常点检测的场景中&#xff0c;我们通常会将数据视为一个类别&#xff08;即正常数据点&#xff09;&#xff0c;并尝试找到那些与正常数据点显著不同的点&#xff08;即异常点&#xff…

jQuery的简单使用

jQuery的简单使用 jQuery查找父、子、兄弟节点jQuery查找内容元素筛选遍历元素操作元素width() / height() 设置宽高.css() 设值样式attr() / prop() 设置属性增加、删除、切换class删除和清空 操作元素总结选择表达式链式操作取值和赋值函数 HTML_1 <table id"table_…

利用大模型提升个性化推荐的异构知识融合方法

在推荐系统中&#xff0c;分析和挖掘用户行为是至关重要的&#xff0c;尤其是在美团外卖这样的平台上&#xff0c;用户行为表现出多样性&#xff0c;包括不同的行为主体&#xff08;如商家和产品&#xff09;、内容&#xff08;如曝光、点击和订单&#xff09;和场景&#xff0…

C++奇迹之旅:C++内存管理的机制(终篇)

文章目录 &#x1f4dd;malloc/free和new/delete的区别&#x1f6a8; 内存泄漏&#x1f3af;什么是内存泄漏&#xff0c;内存泄漏的危害 &#x1f3af;内存泄漏分类&#x1f3af;如何检测内存泄漏&#x1f3af;如何避免内存泄漏 &#x1f6a9;总结 &#x1f4dd;malloc/free和n…

[XYCTF新生赛]-PWN:guestbook1解析(程序自带栈迁移,off by one覆盖rbp一字节实现栈迁移)

查看保护 查看ida 这里可以覆盖rbp最后一个字节&#xff0c;而且程序会执行两次leave ret 完整exp&#xff1a; from pwn import* #pprocess(./guestbook) premote(gz.imxbt.cn,20073) backdoor0x00401328for i in range(33):p.sendlineafter(index,str(i))payloadp64(backdo…

多模态大语言模型和 Apple 的 MM1

原文地址&#xff1a;multimodal-large-language-models-apples-mm1 2024 年 4 月 13 日 抽象是计算机科学中最关键的概念之一&#xff0c;具有一些最强大的影响。从简单的角度来看&#xff0c;抽象就是将某一事物应用于多种不同情况的能力。例如&#xff0c;如果你创造了一种…

如何让 PDF 书签从杂乱无序整洁到明丽清新

1、拉取书签&#xff08;详细步骤看文末扩展阅读&#xff09; 原状态 —— 杂乱无序 自动整理后的状态 —— 错落有致&#xff0c;但摩肩接踵 2、开始整理 全选自动整理后的书签&#xff0c;剪切 访问中英混排排版优化 - 油条工具箱 https://utils.fun/cn-en 1 粘贴 → 2 …

R语言数据探索和分析7-使用随机森林模型对中国GDP及其影响因素分析

一、研究背景和意义 国内生产总值&#xff08;GDP&#xff09;是宏观经济领域中最为关注的经济统计数据之一&#xff0c;它反映了一个国家或地区在一定时期内所创造的所有最终商品和服务的总价值。GDP的增长率不仅仅是一个国家经济健康状况的关键指标&#xff0c;还直接关系到…

leetCode68. 文本左右对齐

基本思路&#xff1a; leetCode68. 文本左右对齐 代码 class Solution { public:vector<string> fullJustify(vector<string>& words, int maxWidth) {vector<string> res;for(int i 0; i < words.size(); i){ // 枚举有多少个单词int j i 1; //…

【JVM】内存调优——内存泄漏、内存溢出

内存调优 什么是内存泄漏、内存泄漏&#xff1f; 内存泄漏&#xff1a;在Java中如果不再使用一个对象&#xff0c;但是该对象依然在GC ROOT的引用链上&#xff0c;这个对象就不会被垃圾回收器回收。内存溢出&#xff1a;内存的使用量超过了Java虚拟机可以分配的上限&#xff…

数字孪生不能接入数据,那还叫数字孪生吗?花架子而已

Hi&#xff0c;我是贝格前端工场&#xff0c;经常给大家分享可视化和数字孪生项目&#xff0c;就有一些老铁说这个不能接入真实数据&#xff0c;是个花架子。我就乐啦&#xff0c;不接入数据那还叫数字孪生吗&#xff1f; 数字孪生是指通过数字化技术对实体世界进行建模、仿真和…

[1702]java旅游资源网上填报系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java旅游资源网上填报系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql…

「 网络安全常用术语解读 」SBOM主流格式SWID详解

国际标准化组织&#xff08;ISO&#xff09;和国际电工委员会&#xff08;International Electrotechnical Commission&#xff0c;IEC&#xff09;发布了ISO/IEC 19770-2软件标识&#xff08;Software Identification&#xff0c;SWID&#xff09;标签标准&#xff0c;该标准定…

数据结构与算法---线性表

线性表 1.顺序表 需求分析 /*创建顺序表具体功能&#xff1a;初始化顺序表销毁顺序表获取顺序表元素个数输出顺序表中的内容自动扩容增 --- 插入数据&#xff08;包含了尾部添加功能&#xff09;删 --- 删除数据&#xff08;包含了尾部删除功能&#xff09;改 --- 修改数据查…

(ARM)ORACLE JDK 22 的下载安装及环境变量的配置

目录 获取JDK 安装JDK 配置JAVA环境变量 其他补充&#xff1a;JDK 22的新特征 1. 语法 2. 库 3. 性能 4. 工具 在今年的3月份&#xff0c;ORACLE 更新了的JDK 发行版 JDK 22&#xff0c;作为了一位ORACLE Primavera系列产品的研究者&#xff0c;其实对JDK的迭代完全不感…

基于.NET WinForms 数据的CURD实现

开发工具 VS 2022 C#&#xff0c;数据库MS SQL SERVER 2019 1.WinForms界面 2.使用SqlDataApater DataSet DataGridView 读取数据 private void ReadData() {//数据库连接串string strConn "Data Source127.0.0.1;Initial CatalogTEST;Persist Security InfoTrue;Us…

Vue 组件通信

组件通信 组件与组件之间的数据传递 组件的数据是独立的&#xff0c;无法直接访问其他组件的数据。通过组件通信&#xff0c;可以访问其他组件的数据。 组件关系 父子关系非父子关系 组件通信解决方案 父子关系 父->子 父组件通过props将数据传递给子组件 App.vue …
最新文章