在线OJ系统
设计思路:
仿照LeetCode、牛客,实现一个在线代码编译系统。
模块划分:
- 在线编译器模块
- 题目管理模块
- 工具模块
- 日志模块
在线编译器模块
此模块主要实现:获取用户提交的代码,调用 gcc/g++ 编译代码,并返回执行结果
-
获取用户提交的代码
- 使用第三方库 cpp-httplib 搭建http服务器
- 封装编译API
- 使用JSON格式序列化网页中收集到的数据
-
调用gcc、g++编译代码
- 创建子进程,程序替换编译代码
- 创建子进程,程序替换运行代码
- 重定向结果编译结果到临时文件
-
组织结果,进行返回
- 将临时文件中的信息序列化为JSON格式
- 使用ctemplate模版技术渲染html页面
- 将结果返回给用户
题目管理模块
此模块主要实现:本地题目的组织与加载
- 组织
- 通过结构体记录题目的id、name、star、dir
- 将题目的代码框架、测试用例、题目描述的信息以文件的形式存储在本地,并将储存路径写入dir
- 加载
- 以 k(id) : v(结构体) 的方式将题目信息加载到map中
- 封装API以供上层调用
工具模块
此模块主要实现:封装文件、URL解码工具
具体实现见下面代码
日志模块
此模块用于记录服务器信息
以 "[日志级别+时间戳+文件名:行号] "的格式打印信息
项目中日志级别约定:INFO(提示)、WRAING(警告)、ERROR(错误)、FATAL(致命)
遇到的问题:
- gcc未升级,不支持C++11正则表达式
- 未关闭linux防火墙导致浏览器无法访问页面
- 安装ctemplate第三方库
- 编译时指定路径寻找ctemplate,没有加-lctemplate
代码实现:
oj_server.cpp
int main()
{
//服务器启动时,只加载一次数据
OjModel model;
model.Load();
using namespace httplib;
Server server;
server.Get("/all_questions", [&model](const Request &req, Response &resp){
(void) req;
std::vector<Question> all_questions;
model.GetAllQuestions(&all_questions);
std::string html;
OjView::RenderAllQuestions(all_questions, &html);
resp.set_content(html, "text/html");
});
//R"()" C++11引入原始字符串--->忽略字符串中的转意字符
// \d+--->正则表达式
server.Get(R"(/question/(\d+))", [&model](const Request &req, Response &resp){
Question question;
model.GetQuestion(req.matches[1].str(), &question);
std::string html;
OjView::RenderOneQuestion(question, &html);
resp.set_content(html, "text/html");
});
server.Post(R"(/compile/(\d+))", [&model](const Request &req, Response &resp){
//1.根据id获取题目信息
Question question;
model.GetQuestion(req.matches[1].str(), &question);
//2.解析body,获取用户提交的代码
std::unordered_map<std::string, std::string> kv;
UrlUtil::ParseBody(req.body, &kv);
const std::string &user_code = kv["code"];
//3.构造JSON结构的参数
Json::Value req_json;
std::string tail;
FileUtil::Read(question.dir + "/tail.cpp", &tail);
req_json["code"] = user_code + tail;
Json::Value resp_json;
//4.调用编译模块编译
Compiler::CompileAndRun(req_json, &resp_json);
//5.根据编译结果构造html页面
std::string html;
OjView::RenderResult(resp_json["stdout"].asString(), resp_json["reason"].asString(), &html);
resp.set_content(html, "text/html");
});
server.listen("0.0.0.0", 9527);
return 0;
}
util.hpp
#pragma once
#include <stdio.h>
#include <stdint.h>
#include <sys/time.h>
#include <iostream>
#include <string>
#include <