在线 OJ 项目(一) · 项目介绍 · 进程与线程 · 实现编译运行模块

news2024/12/23 22:12:16

  • 一、项目介绍
  • 二、导入依赖、创建基本项目结构
    • 导入依赖
    • 创建基本项目结构
  • 三、进程、线程的基础知识回顾
  • 四、封装操作进程的工具类
  • 五、实现 “编译运行” 模块 Task 类
  • 六、封装读写文件的方法
    • 修改 JDK 版本
  • 七、Task 类的实现
  • 八、整理一下项目列表

一、项目介绍

项目实现一个在线 OJ 平台,核心功能:

  1. 能够管理题目(保存很多题目信息)。
  2. 题目列表页:能够展示题目列表。
  3. 题目详情页:能够展示某个题的详细信息 + 代码编辑框。
  4. 提交并运行题目:详情页中有一个 “提交” 按钮,点击按钮网页就会把当前的代码给提交到服务器上。服务器会执行代码,并且给出一些是否通过用例的结果。
  5. 查看运行结果:有另外一个结果页面,能展示提交是否通过,以及错误的用例信息。


二、导入依赖、创建基本项目结构

导入依赖

新建 maven 项目,在 pom.xml 中导入依赖

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

创建基本项目结构


三、进程、线程的基础知识回顾

进程简介:

进程也可以称为是 “任务”,操作系统想要执行一个具体的 “动作” 就要创建出一个对应的进程。

一个程序没有运行的时候,仅仅只是一个 “可执行文件”;一个程序跑起来的时候,就变成一个进程了。

为了实现 “并发编程”,同时执行多个任务,就引入了 “多进程编程”。
把一个很大的任务,拆分成若干个很小的任务,创建多个进程,每个进程分别负责其中的一部分任务。

也带来了一个问题:创建 / 销毁进程,比较低效。怎么办呢?就引入了线程。

线程简介:

每个线程都是一个独立的执行流,一个进程包含了一个或者多个线程。
创建 / 销毁线程 比 创建 / 销毁进程更加高效。
因此,在 Java 中大部分并发编程都是通过多线程的方式来实现的。

二者对比:

可是,进程相比于线程,有着 “独立性” 的优势。
操作系统上,同一时刻运行着很多个进程,如果某个进程挂了,不会影响到其它进程(类似于你微信崩溃了,不会影响到 QQ 的使用)。

相比之下,由于多个线程之间,共用着同一个进程的地址空间,某个线程挂了,就很有可能把整个进程带走。

回到 OJ 项目,分析多进程与多线程:

在线 OJ,有一个服务器进程,运行着 Servlet,接收用户的请求,返回响应…

用户提交的代码模块,也是一个独立的逻辑。这个逻辑要使用多线程执行好?还是多进程呢?

对于用户提交的代码,一定要通过 “多进程” 的方式来执行~
因为我们无法控制用户提交了什么代码,代码可能存在很多问题,很可能一运行就崩溃!
如果使用多线程,就会导致用户代码直接导致整个服务器崩溃的情况~

所以我们要使用多进程编程。

Java 中进行多进程编程

多进程编程主要做的事情:

站在操作系统的角度(以 Linux 为例),提供了很多和多进程编程相关的接口。
进程创建、进程终止、进程等待、进程程序替换、进程间通信…

而 Java 中对系统提供的这些操作进行了限制,最终给用户只提供了两个操作:进程创建、进程等待。

测试进程的代码

	public static void main(String[] args) throws IOException {
	    Runtime runtime = Runtime.getRuntime();
	    // 执行这个代码,相当于对着 cmd 中输入了一个 javac 命令
	    Process process = runtime.exec("javac");
	}

显然,操作系统不认识这个 javac 命令是啥。
如果把 javac 改为 notepad,操作系统就会帮我们打开一个记事本。

	public static void main(String[] args) throws IOException {
	    Runtime runtime = Runtime.getRuntime();
	    // 执行这个代码,相当于对着 cmd 中输入了一个 javac 命令
	    Process process = runtime.exec("notepad");
	}

咱们输入的命令,操作系统会去一些特定目录中找,看看是否存在与之对应的可执行文件。

解决前面 javac 问题,需要把 javac 所在的目录加入到 PATH 环境变量中,就一开始学 Java 要配置的环境变量。

配置成功,输入 javac 就可以显示列表了。

javac 是一个控制台程序,它的输出是输出到 “标准输出” 和 “标准错误” 这两个特殊的文件中的。
想要看到这个程序的运行效果,就要获取到标准输出和标准错误的内容~

一个进程在启动的时候,就会自动打开三个文件:

  1. 标准输入 - 对应到键盘
  2. 标准输出 - 对应到显示器
  3. 标准错误 - 对应到显示器

虽然子进程启动后,同样也打开了这三个文件,可是由于子进程没有和 IDEA 的终端关联。因此在 IDEA 中是看不到这些文件的,我们需要手动在代码中获取。

通过文件的方式,获取进程中的标准输出和标准错误

public static void main(String[] args) throws IOException {
   Runtime runtime = Runtime.getRuntime();
    // process 表示 “进程”
    Process process = runtime.exec("javac");

    // 获取子进程的标准输出和标准错误,把这里的内容写入到两个文件中
    // 获取标准输出
    InputStream stdoutFrom = process.getInputStream();
    FileOutputStream stdoutTo = new FileOutputStream("stdout.txt");
    while (true) {  // 循环读取进程中的标准输出,写入到文件中
        int ch = stdoutFrom.read();
        if (ch == -1) {
            break;
        }
        stdoutTo.write(ch);
    }
    // 关闭流
    stdoutFrom.close();
    stdoutTo.close();

    // 获取标准错误, 从这个文件对象中读, 就能把子进程的标准错误给读出来!
    InputStream stderrFrom = process.getErrorStream();
    FileOutputStream stderrTo = new FileOutputStream("stderr.txt");
    while (true) {
        int ch = stderrFrom.read();
        if (ch == -1) {
            break;
        }
        stderrTo.write(ch);
    }
    stderrFrom.close();
    stderrTo.close();
}

此时目录会生成两个文件,把进程中的标准输出和标准错误读取到文件中了。

进程等待

通过这个代码,能创建出子进程,但是此时父子进程之间是 “并发执行” 的关系。另一方面,往往也需要让父进程知道子进程的执行状态。

在当前场景中,希望父进程等待子进程执行完毕之后,再执行后续代码。
像在线 OJ 系统,需要让用户提交代码,编译执行代码完毕后,再把响应返回给用户。

 // 通过 Process 类的 waitFor 方法来实现进程的等待.
 // 父进程执行到 waitFor 的时候, 就会阻塞. 一直阻塞到子进程执行完毕为止.
 // (和 Thread.join 是非常类似的)
 // 这个退出码 就表示子进程的执行结果是否 ok. 如果子进程是代码执行完了正常退出, 此时返回的退出码就是 0.
 // 如果子进程代码执行了一半异常退出(抛异常), 此时返回的退出码就非 0.
 int exitCode = process.waitFor();
 System.out.println(exitCode);

四、封装操作进程的工具类

复习完进程的相关知识,我们需要把系统关于进程的操作封装成一个工具类。

CommandUtil

public class CommandUtil {
    // 1. 通过 Runtime 类得到 Runtime 实例,执行 exec 方法
    // 2. 获取到标准输出,写入到指定文件中
    // 3. 获取到标准错误,写入到指定文件中
    // 4. 等待子进程结束,拿到子进程状态码,并返回
    public static int run(String cmd, String stdoutFile, String stderrFile) {
        try {
            // 1. 通过 Runtime 类得到 Runtime 实例,执行 exec 方法
            Process process = Runtime.getRuntime().exec(cmd);
            // 2. 获取到标准输出,写入到指定文件中
            if (stdoutFile != null) {
                InputStream stdoutFrom = process.getInputStream();
                FileOutputStream stdoutTo = new FileOutputStream(stdoutFile);
                while (true) {
                    int ch = stdoutFrom.read();
                    if (ch == -1) {
                        break;
                    }
                    stdoutTo.write(ch);
                }
                stdoutFrom.close();
                stdoutTo.close();
            }
            // 3. 获取到标准错误,写入到指定文件中
            if (stderrFile != null) {
                InputStream stderrFrom = process.getInputStream();
                FileOutputStream stderrTo = new FileOutputStream(stderrFile);
                while (true) {
                    int ch = stderrFrom.read();
                    if (ch == -1) {
                        break;
                    }
                    stderrTo.write(ch);
                }
                stderrFrom.close();
                stderrTo.close();
            }
            // 4. 等待子进程结束,拿到子进程状态码,并返回
            int exitCode = process.waitFor();
            return exitCode;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return 1;
    }
}

对 CommandUtil 类进行测试
在 CommandUtil 类创建 main 方法,调用 run 方法,查看是否能生成文件。

public static void main(String[] args) {
    CommandUtil.run("javac", "stdout.txt", "stderr.txt");
}

报错了…

回到代码中发现,文件流都没有写错,标准输出和标准错误没有混淆。最后发现是标准错误中的进程 process 调用错了,把调用了上面的标准输出的数据。

修改过后,可以成功运行,也能查看到生成文件中的内容了。


五、实现 “编译运行” 模块 Task 类

接下来,基于准备好的 CommandUtil,实现一个完整的 “编译运行” 模块。

需要做的事情:

  1. 用户提交的代码(输入).
  2. 程序的编译结果和运行结果(输出).

创建一个承载需要编译代码的实体类 Question。

// 此类表示一个 task 的输入内容,包含需要编译的代码
public class Question {
    private String code;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

创建一个存储编译运行结果的实体类 Answer。

// 此类表示 Task 的执行结果
public class Answer {
    // 错误码。error 为 0 表示编译运行通过;为 1 表示编译出错;为 2 表示运行出错。
    private int error;
    // 出错提示信息,根据错误码,存储不同的错误信息。
    private String reason;
    // 运行程序得到的标准输出的结果。
    private String stdout;
    // 运行程序得到的标准错误的结果。
    private String stderr;

    public int getError() {
        return error;
    }

    public void setError(int error) {
        this.error = error;
    }

    public String getReason() {
        return reason;
    }

    public void setReason(String reason) {
        this.reason = reason;
    }

    public String getStdout() {
        return stdout;
    }

    public void setStdout(String stdout) {
        this.stdout = stdout;
    }

    public String getStderr() {
        return stderr;
    }

    public void setStderr(String stderr) {
        this.stderr = stderr;
    }

    @Override
    public String toString() {
        return "Answer{" +
                "error=" + error +
                ", reason='" + reason + '\'' +
                ", stdout='" + stdout + '\'' +
                ", stderr='" + stderr + '\'' +
                '}';
    }
}

由于 Java 中,类名要和文件名一致。
用户提交的类名字,就需要和写入的文件名一致,就可以约定,类名和文件名都叫做 Solution。
类似于 leetcode 刷题中提供好的代码一样:

约定临时文件名字

接下来,我们先通过一组常量来约定临时文件的名字。

// 表示所有临时文件所在的目录
private static final String WORK_DIR = "./tmp1/";
// 约定代码的类名
private static final String CLASS = "Solution";
// 约定要编译的代码文件名
private static final String CODE = WORK_DIR + "Solution.java";
// 约定存放编译错误信息的文件名
private static final String COMPILE_ERROR = WORK_DIR + "compileError.txt";
// 约定存放运行时标准输出的文件名
private static final String STDOUT = WORK_DIR + "stdout.txt";
// 存放运行时标准错误的文件名
private static final String STDERR = WORK_DIR + "stderr.txt";

为什么要搞这么多临时文件呢?

主要是为了 “进程间通信”。
进程与进程之间是存在独立性的,一个进程很难影响到其它进程。

这里我们采取一种简单粗暴的方式进行通信,就是通过文件~

管道、消息队列、信号、Socket、文件…
只要某个东西可以被多个进程同时访问到,就可以用来进行进程间通信~


六、封装读写文件的方法

对读写文件操作进一步封装。
提供两个方法,一个负责读取整个文件内容,返回一个字符串;
另一个方法负责写入整个字符串到文件中。

对于文本文件来说,字符流会比字节流省事很多,不需要手动处理编码格式,尤其是文件中包含中文的时候。

FileUtil

public class FileUtil {
    // 负责把 filePath 对应的文件内容读取出来,放到返回值中
    public static String readFile(String filePath) {
    	//StringBuiler 是线程不安全的,但是效率高
        StringBuilder result = new StringBuilder();
        // 此写法不需要关闭流
        try (FileReader fileReader = new FileReader(filePath)){
            while (true) {
                int ch = fileReader.read();
                if (ch == -1) {
                    break;
                }
                result.append((char)ch);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result.toString();
    }

    // 负责把 content 写入到 filePath 对应的文件中
    public static void writeFile(String content, String filePath) {
        try(FileWriter fileWriter = new FileWriter(filePath)) {
            fileWriter.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        FileUtil.writeFile("hello world", "d:/test.txt");
        String content = FileUtil.readFile("d:/test.txt");
        System.out.println(content);
    }
}

能够在 D 盘看到 test.txt 文件,打开里面有 hello world 就🆗~

测试的时候,莫名其妙又挂了.

在这里插入图片描述

可能是将字符流代码放在 try 的括号里面导致的,需要修改 JDK 版本,保证以下三处 JDK 版本一致就不会报错了~

修改 JDK 版本

查看项目的 JDK 版本

查看工程的 JDK 版本

查看 IDEA 编辑器的 JDK 版本


七、Task 类的实现

之前的操作都是为了 Task 类准备的。

实现保存源代码文件,并测试该方法

// 此类的核心方法。参数:要编译运行的 Java 源代码;返回值:表示编译运行结果。
public Answer compileAndRun(Question question) {
    Answer answer = new Answer();
    // 0. 准备好用来存放临时文件的目录
    File workDir = new File(WORK_DIR);
    // 判断是否存在该目录
    if (!workDir.exists()) {
        // 不存在则创建多级目录.
        workDir.mkdirs();
    }

    // 1. 把 question 中的 code 写入到一个 Solution.java 文件中
    FileUtil.writeFile(question.getCode(), CODE);

    // 2. 创建子进程,调用 javac 进行编译。编译的时候,需要有一个 .java 文件
    //      如果编译出错,javac 就会把错误信息写入到 stderr 里,使用专门的文件来保存:compileError.txt
    
    // 3. 创建子进程,调用 java 命令执行
    //      运行程序的时候,也会把 java 子进程的标准输出和标准错误获取到. stdout.txt, stderr.txt
    
    // 4. 父进程获取到刚才的编译执行结果,并打包成 Answer 对象
    //      编译执行的结果,就通过刚才约定的文件来进行获取
    return null;
}

public static void main(String[] args) {
    Task task = new Task();
    // 待编译代码
    Question question = new Question();
    question.setCode("public class Solution {\n" +
            "    public static void main(String[] args) {\n" +
            "        System.out.println(\"hello world\");\n" +
            "    }\n" +
            "}\n");
    // 编译运行后的结果
    Answer answer = task.compileAndRun(question);
    System.out.println(answer);
}

ok~
经过单元测试发现并没有什么问题,继续~

创建子进程,调用 javac 进行编译。

我们先来看看 javac 进行编译的命令.

javac -encoding utf8 ./tmp/Solution.java -d ./tmp/

指定字符集:-encoding utf8
需要编译的文件:./tmp/Solution.java
指定生成的.class文件存放位置:./tmp/

如果不指定好位置,.class文件可能就跑到别的地方了,后面再进行运行就不方便。

// 2. 创建子进程,调用 javac 进行编译。编译的时候,需要有一个 .java 文件
//      如果编译出错,javac 就会把错误信息写入到 stderr 里,使用专门的文件来保存:compileError.txt
String compileCmd = String.format("javac -encoding utf8 %s -d %s", CODE, WORK_DIR);
System.out.println("编译命令:" + compileCmd);

对于 Java 进程来说,它的标准输出我们不必关注,而是关注标准错误。
一旦编译出错,内容就会通过标准错误来反馈。

CommandUtil.run(compileCmd, null, COMPILE_ERROR);

完成编译模块的代码

// 2. 创建子进程,调用 javac 进行编译。编译的时候,需要有一个 .java 文件
//      如果编译出错,javac 就会把错误信息写入到 stderr 里,使用专门的文件来保存:compileError.txt
String compileCmd = String.format("javac -encoding utf8 %s -d %s", CODE, WORK_DIR);
System.out.println(compileCmd);
CommandUtil.run(compileCmd, null, COMPILE_ERROR);
// 如果编译出错,错误信息就被记录到 COMPILE_ERROR 这个文件中。如果没有编译出错,该文件为空。
String compileError = FileUtil.readFile(COMPILE_ERROR);
if (!compileError.equals("")) {
    System.out.println("编译出错!");
    answer.setError(1);
    answer.setReason(compileError);
    return answer;
}

后续运行 java 命令的代码和编译时差不多,就一次性放出来。

完整的 Task 类

// 编译运行
public class Task {

    // 通过一组常量来约定临时文件的名字
    // 表示所有临时文件所在的目录
    private static final String WORK_DIR = "./tmp1/";
    // 约定代码的类名
    private static final String CLASS = "Solution";
    // 约定要编译的代码文件名
    private static final String CODE = WORK_DIR + "Solution.java";
    // 约定存放编译错误信息的文件名
    private static final String COMPILE_ERROR = WORK_DIR + "compileError.txt";
    // 约定存放运行时标准输出的文件名
    private static final String STDOUT = WORK_DIR + "stdout.txt";
    // 存放运行时标准错误的文件名
    private static final String STDERR = WORK_DIR + "stderr.txt";

    // 此类的核心方法。
    // 参数:要编译运行的 Java 源代码;
    // 返回值:表示编译运行结果。
    public Answer compileAndRun(Question question) {
        Answer answer = new Answer();
        // 0. 准备好用来存放临时文件的目录
        File workDir = new File(WORK_DIR);
        // 判断是否存在该目录
        if (!workDir.exists()) {
            // 不存在则创建多级目录.
            workDir.mkdirs();
        }

        // 1. 把 question 中的 code 写入到一个 Solution.java 文件中
        FileUtil.writeFile(question.getCode(), CODE);

        // 2. 创建子进程,调用 javac 进行编译。编译的时候,需要有一个 .java 文件
        //      如果编译出错,javac 就会把错误信息写入到 stderr 里,使用专门的文件来保存:compileError.txt
        String compileCmd = String.format("javac -encoding utf8 %s -d %s", CODE, WORK_DIR);
        System.out.println("编译时:" + compileCmd);
        CommandUtil.run(compileCmd, null, COMPILE_ERROR);
        // 如果编译出错,错误信息就被记录到 COMPILE_ERROR 这个文件中。如果没有编译出错,该文件为空。
        String compileError = FileUtil.readFile(COMPILE_ERROR);
        if (!compileError.equals("")) {
            System.out.println("编译出错!");
            answer.setError(1);
            answer.setReason(compileError);
            return answer;
        }

        // 3. 创建子进程,调用 java 命令执行
        //      运行程序的时候,也会把 java 子进程的标准输出和标准错误获取到. stdout.txt, stderr.txt
        String runCmd = String.format("java -classpath %s %s", WORK_DIR, CLASS);
        System.out.println("运行时:" + runCmd);
        CommandUtil.run(runCmd, STDOUT, STDERR);
        String runError = FileUtil.readFile(STDERR);
        if (!runError.equals("")) {
            System.out.println("运行时错误!");
            answer.setError(2);
            answer.setReason(runError);
            return answer;
        }

        // 4. 父进程获取到刚才的编译执行结果,并打包成 Answer 对象
        //      正常编译运行的结果,就通过刚才约定的文件来进行获取
        answer.setError(0);
        answer.setReason(FileUtil.readFile(STDOUT));

        return answer;
    }

    public static void main(String[] args) {
        Task task = new Task();
        // 待编译代码
        Question question = new Question();
        question.setCode("public class Solution {\n" +
                "    public static void main(String[] args) {\n" +
                "        System.out.println(\"hello world\");\n" +
                "    }\n" +
                "}\n");
        // 编译运行后的结果
        Answer answer = task.compileAndRun(question);
        System.out.println(answer);
    }
}

通过单元测试,方法没问题。


八、整理一下项目列表

修改一下项目列表
有点乱了…

api 用于前后端交互 Servlet.
common 存放工具类.
compile 存放编译运行模块.
dao 层存储实体类.

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

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

相关文章

煤矿AI智能视频分析识别系统 opencv

煤矿AI智能视频分析识别系统通过opencvpython 深度学习网络模型&#xff0c;对皮带跑偏、撕裂、堆煤、异物、非法运人、有煤无煤状态等异常情况&#xff0c;以及人员工服穿戴、反光衣、安全帽、睡岗离岗、打电话、抽烟等行为进行自动抓拍存档。OpenCV基于C实现&#xff0c;同时…

【正点原子FPGA连载】第二十八章Linux并发与竞争 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Linux开发指南

1&#xff09;实验平台&#xff1a;正点原子MPSoC开发板 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id692450874670 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html 第二十八章Linux…

【splishsplash】Houdini粒子的导入与导出

Houdini粒子的导入与导出 Houdini导入到splish 在Houdini中使用file导入任意几何模型 使用points from volume采样点&#xff0c;使其粒子化 使用file导出粒子化之后的模型&#xff0c;后缀写bhclassic 创建json场景文件&#xff08;建议放到MyScences文件夹&#xff09; …

C语言指针是什么?

计算机中所有的数据都必须放在内存中&#xff0c;不同类型的数据占用的字节数不一样&#xff0c;例如 int 占用 4 个字节&#xff0c;char 占用 1 个字节。为了正确地访问这些数据&#xff0c;必须为每个字节都编上号码&#xff0c;就像门牌号、身份证号一样&#xff0c;每个字…

HTTP协议 | 青训营笔记

1、概述 HTTP协议&#xff0c;超文本传输协议 应用层的协议&#xff0c;基于TCP协议&#xff0c;简单可扩展&#xff08;可以自定义header&#xff09; 每个HTTP请求都可以分为请求和响应两个部分 无状态的&#xff08;不知道之前的请求是携带过什么信息&#xff09; 2、协…

【Qt】8.QPainter、高级设置、手动调用绘图事件、绘图设备、文件操作、文件信息

目录 QPainter 代码 widget.h widget.cpp 结果 高级设置 代码 widget.h widget.cpp 结果 手动调用绘图事件 代码 widget.h widget.cpp 结果 绘图设备 代码 widget.h widget.cpp 结果 文件操作 代码 widget.cpp 结果 文件信息 代码 widget.cpp 结果…

一文带你看懂健康管理系统----IPMI

目录 1. IPMI概述 2. IPMI系统设计 3. 主BMC模块设计 5. 从IPMI模块设计 6. 名词解释 6. 代码 1. IPMI概述 智能平台管理接口&#xff08;IPMI&#xff1a;Intelligent Platform Management Interface&#xff09;是一项应用于服务器管理系统设计的标准&#xff0c;由Int…

BSN-DDC基础网络详解(一):基础介绍

BSN-DDC基础网络推出已经一年了&#xff0c;得到了行业应用方和广大开发者的高度认可。一年中BSN产品技术团队也在根据市场业务需求不断更新功能服务&#xff0c;我们将通过本系列文章为大家系统化介绍DDC网络的功能和使用&#xff0c;为感兴趣的朋友提供学习帮助。BSN-DDC基础…

历史与今日的事件

作者简介&#xff1a;一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 1.2023年1月31日中国最新量子计算机“悟空”即将问世&#xff0c…

【34】C语言 | 动态内存管理

目录 1.为什么存在动态内存分配 2、动态内存函数的介绍 2.1 malloc和free 2.2 calloc 2.3 realloc 1.为什么存在动态内存分配 我们已经掌握的内存开辟方式有: int val 20; //在栈空间上开辟四个字节 char arr[n] {0}; //在栈空间上开辟10个节的连续空间 但是上述的开辟…

mPEG-SS-NHS甲氧基聚乙二醇-二硫键-琥珀酰亚胺酯

mPEG-SS-NHS甲氧基聚乙二醇-双硫键-活性酯 名称&#xff1a;甲氧基聚乙二醇-双硫键-琥珀酰亚胺酯 英文名称&#xff1a;mPEG-SS-NHS 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 用 途&#xff1a;仅供科研实验使用&#xff0c;不用于诊治 外观: 固体或粘性…

【c语言进阶】动态通讯录

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a; c语言学习 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对我…

彭博:预订量未及预期,索尼大幅削减PS VR2首季订单

在索尼VR新品&#xff1a;PS VR2正式发货前夕&#xff0c;彭博社爆料称&#xff1a;因预订量不及预期&#xff0c;索尼已经大幅削减PS VR2首季订单量。消息人士称&#xff0c;索尼PS VR2发布后首个季度的订单目标是200万台&#xff0c;现已根据预订量减半&#xff0c;至约100万…

iOS 视频播放器开发

需求设计 做一个小学生教育辅导视频播放器。 参考小猿搜题视频播放器 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L0GsyFSt-1675164972791)(https://tva1.sinaimg.cn/large/008vxvgGgy1h9xk4fm5xfj31sx0u0mz0.jpg)] [外链图片转存失败,源站可…

Java中的JCA对基于密码的加密进行成功的蛮力搜索攻击所需的时间估计

对于JCA中基于密码的DES加密实现&#xff0c;固定一些盐和迭代计数&#xff0c;并记录加密/解密所需的平均时间;估计成功进行蛮力搜索攻击所需的时间Name: NIHAO DONG Number: 201476606 Password List:N Password 1 P$$W0rD 2 thisismypassword 3 VeryLongP$$W0rD fi…

[0CTF 2016]piapiapia(字符逃逸详解)

目录 知识点 信息收集 尝试SQL注入 源码目录扫描 代码审计 payload生成 知识点 信息泄露参数传递数组绕过字符串检测反序列化字符逃逸 信息收集 收集到了一只超可爱的小喵 尝试SQL注入 用户名不存在的回显 密码无效的回显 用户存在&#xff0c;密码错误的回显 判断闭…

STC15系列PWM功能相关功能寄存器介绍

STC15系列PWM功能相关功能寄存器介绍✨以下数据来源于stc15手册。 &#x1f4d3;增强型PWM波形发生器相关功能寄存器总表 1. 端口配置寄存器&#xff1a;P_SW2 2.PWM配青寄存器:PWMICFG CBTADC: PWM计数器归零时 (CBIF1时) 触发ADC转换 – 0:PWM计数器归零时不触发ADC转换 – …

Web3中文|亚马逊进入web3,将在春季推出NFT计划

亚马逊正向加密行业迈出第一步。 根据Blockworks 1月26日发布的报告&#xff0c;这家电子商务巨头计划在2023年春天推出一项专注于区块链游戏和相关NFT的计划。 该计划仍处于开发阶段&#xff0c;但发布的最后期限定为4月。亚马逊用户将可以体验基于区块链的游戏并领取免费的…

Python采集某乎专栏文章保存成pdf

前言 大家早好、午好、晚好吖 ❤ ~ 环境使用: Python 3.8 Pycharm wkhtmltopdf 软件 --> 文章下方名片信领取 模块使用: requests >>> pip install requests 数据请求 parsel >>> pip install parsel 数据解析 re >>> 内置模块 不需要安装…

域内委派攻击

域委派是指&#xff0c;将域内用户的权限委派给服务账号&#xff0c;使得服务账号能以用户权限开展域内活动。利用委派可获取域管理员权限 域委派主要分为三种&#xff1a; 非约束性委派 约束性委派 基于资源的约束性委派 在Windows系统中&#xff0c;只有服务账号和主机账号…