文章目录
前言
- 系列文章目录
- 前言
- 一、在线OJ系统描述
- 二、在线编译模块
- 1.搭建一个HTTP服务器完成在线编译
- 2.收到HTTP请求,进行数据格式转化(HTTP中body的内容转换为JSON格式的字符串)
- 3.compile_server.cpp浏览器提交JSON数据请求服务器,服务器调用在线编译模块编译,把结果返回给浏览器
- 4.util.hpp工具类
- 1.TimeUtil类时间戳获取工具TimeUtil标识文件的不同
- 2.打印日志的工具
- 3.文件类FileUtil把文件所有内容读取出来,放到content字符串中
- 4.URL body解析模块
- 5.查找用户代码中是否有危害服务器的语句
- 5.compile.hpp在线编译类
- 1.源代码的文件
- 2.编译错误文件
- 3.可执行程序文件
- 4.标准输入文件
- 5.标准输出文件
- 6.标准错误文件
- 7.CompileAndRun函数
- 8.WriteTmpFile函数
- 9.Compile函数
- 10.Run函数
- 6.post提交到所有问题界面
- 7.在线编译模块小结
- 三、题目管理模块
- 1.oj_data存放题目的文件夹
- 1.oj_config.cfg每一行都代表一个题目
- 2.header.cpp代码框架
- 3.tail.cpp代码测试用例
- 4.desc.txt题目详细描述
- 2.MVC中的M负责存储数据 oj_model.hpp这个oj_model模块做的事情,就是把刚才文件中存储的题目信息加载起来,供服务器使用
- 1.描述题目的类question
- 2.load函数表示把文件中的数据加载到内存中, 加到哈希表中
- 3.GetAllQuestion获取所有题目
- 4.GetQuestion获取某个具体的题目
- 3.MVC中的C controller: 核心业务逻辑 oj_server.cpp作为服务器的核心逻辑,需要创建好对应的服务器框架代码,在这个框架中来组织逻辑
- 1.all_questions核心业务逻辑
- 2.question核心业务逻辑
- 3.compile核心业务逻辑
- 4.oj_server.hpp对于oj_server.cpp的实现部分controller 作为服务器的核心逻辑,需要创建好对应的服务器框架代码,在这个框架中来组织逻辑
- 1.server.Get("/all_questions")
- 2.server.Get(R"(/question/(\d+))"
- 3.server.Post(R"(/compile/(\d+))"
- 5.MVC中的v V => view : 负责显示界面 oj_view.hpp根据数据,生成html这个动作,通常叫做网页渲染(render)
- 1.RenderAllQuestion渲染所有问题界面
- 2.RenderQuestion渲染单个问题界面
- 3.RenderCompileResult渲染结果界面
- 三、前端结果界面
- 1.all_question.html
- 2.question.html
- 3.result.html
- 总结
实现一个在线OJ系统类似于力扣或者牛客网的核心部分刷题代码练习功能,提供了用户一个可以在线刷题编写代码并且能够进行编译运行的环境,题目通过序号排序,题目也有难度等级的划分,测试用例等等。在编写代码的同时提供了语法纠错、代码高亮、自动补全等基本功能。
用户可以通过域名加上端口号访问服务器,系统内置了多道编程题,用户点击对应题目就可以进行练习,并且题目内含有大量测试样例。服务器端会根据用户编写代码会进行用例的测试,检测用户代码是否符合题意,并且可以将编译成功结果或者编译出错的原因返回给浏览器端。
二、在线编译模块
在现编译模块的实现:此模块的核心完成"在线",用户把写好的代码通过网页提交到服务器上,服务器调用g++完成编译过程,并且调用刚生成的可执行程序,验证程序结果,返回给用户提交的浏览器上。
搭建一个HTTP服务器来完成在线编译的核心功能。
此处开源的Httplib源代码:cpp-httplib 或者直接git clone: git clone https://github.com/yhirose/cpp-httplib
A C++11 single-file header-only cross platform HTTP/HTTPS library.
It's extremely easy to setup. Just include the httplib.h file in your code!
NOTE: This is a multi-threaded 'blocking' HTTP library. If you are looking for a 'non-blocking' library, this is not the one that you want.
翻译:一个C++11单文件头文件跨平台HTTP/HTTPS库。
它非常容易安装。只需在代码中包含httplib.h文件即可!
注意:这是一个多线程的“阻塞”HTTP库。如果你正在寻找一个“非阻塞”库,这不是你想要的。
快速上手一个开源项目小技巧:
- Json如何从req请求中获取到Json请求?
- 从req对象中获取到。
- Json如何和Http协议结合?
- 需要的请求格式是json格式,HTTP能够提供的格式,是另外一种键值对的 格式,所以要对HTTP提供的格式进行格式的转换。
- 此处由于提交的代码中可能会包含一些特殊符号,这些特殊符号要正确传 输时,就会进行urlencode,这一步由浏览器自动完成。
- 服务器收到请求之后的第一件事就是切分键值对,再urldecode,然后解> 析数据,整理成需要的Json格式。
- Json如何进行解析和构造?
- 使用jsoncpp第三方库。
jsoncpp第三方库获取办法:
1 #include
2
3 #include "httplib.h"
4 #include "compile.hpp"
5 //#include "jsoncpp/json/json.h"
6
7 #include
8
9 int main()
10 {
11 using namespace httplib;
12
13 Server server;
14
15 // Get注册一个回调函数,这个函数的调用机制是处理Get方法时
16 // lambda表达式 就是一个匿名函数
17
18 // 路由过程
19 // 接收请求对象,根据请求进行处理,并且将响应返回给客户端
20 //
21 // 此处get改成post,代码放到body里面
22 server.Post("/compile", [](const Request& req, Response& resp) {
23 // 根据具体的问题场景,根据请求,计算出响应结果
24 (void)req;
25
26 // 如何从req请求中获取到Json请求
27 // Json如何和Http协议结合
28
29 // 需要的请求格式是json格式,HTTP能够提供的格式,是另外一种键值对的格式,所以要对HTTP提供的格式进行格式的转换
30
31 // 此处由于提交的代码中可能会包含一些特殊符号,这些特殊符号要正确传输时,就会进行urlencode,这一步由浏览器自动完成
32 // 服务器收到请求之后的第一件事就是切分键值对,再urldecode,然后解析数据,整理成需要的Json格式
33
34 // 此函数将Http中的body,解析成键值对,存入body_kv
35 std::unordered_map body_kv;
36 UrlUtil::ParseBody(req.body, &body_kv);
37
38 // Json如何进行解析和构造? 使用jsoncpp第三方库
39 //
40 // 下方调用CompileAndRun
41
42 Json::Value req_json; // 从req对象中获取到
43
44 /* for(std::unordered_map::iterator it=body_kv.begin();it!=body_kv.end();++it)
45 {
46 req_json[it->first]=it->second;
47 }
48 */
49
50 for (auto e : body_kv)
51 {
52 // e的类型和 *it 是一致的
53 req_json[e.first] = e.second;
54 }
55
56 Json::Value resp_json; // resp_json发到响应中
57
58 // resp_json 输出型参数
59 Compiler::CompileAndRun(req_json, &resp_json);
60
61 // 把Json::Value对象序列化成为字符串,才能返回
62 Json::FastWriter writer;
63 resp.set_content(writer.write(resp_json), "text/plain");
64 });
65
66 // 让浏览器能访问到一个静态页面
67 // 静态页面: index.html 不会发生变化
68 // 动态页面: 编译结果 随着参数的不同而发生变化
69 //
70 // 加上这个目录是为了浏览器能够访问到静态页面
71 server.set_base_dir("../wwwroot", "");
72 server.listen("0.0.0.0", 9092);
73
74 return 0;
75 }
18 class TimeUtil
19 {
20 public:
21 // 获取当前时间戳
22 static int64_t TimeStamp()
23 {
24 struct timeval tv;
25 ::gettimeofday(&tv, nullptr);
26
27 return tv.tv_sec;
28 }
29
30 static int64_t TimeStampMS()
31 {
32 struct timeval tv;
33 ::gettimeofday(&tv, nullptr);
34
35 return tv.tv_sec * 1000 + tv.tv_usec / 1000;
36 }
37 };
39
40 // 打印日志的工具
41
42 // 期望打印出的日志格式:
43 // [I时间戳 util.hpp:31] hello
44 // 日志的使用方式形如: LOG(INFO)
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?