项目思路
之前写过一个基于http的在线编译器,可以完成代码的编译过程,但是在leetcode上刷题的时候忽然想到在线OJ不就是一个功能更强大的在线编译器吗?在基础的编译功能上加上了题目列表页的展示,每个题目都有对应的序号,名称以及描述,于是想着能不能自己对这个项目再进行进一步的扩充
要实现一个简易的在线OJ,有几个大的模块需要去实现,最主要的是在线编译,编译功能可以在服务器上使用子进程进程替换使用g++指令来完成,题目存储在数据库中,当服务器启动就把题目从数据库中加载到内存上,客户端请求哪个题目就将题目的具体信息经过渲染返回给客户端,客户端通过题目描述将具体的代码上传,最终得出结果。
具体实现
在线编译模块
- 在线测评,势必要使用http服务器来支持在线功能,这里使用cpp-httplib来完成对http服务器的搭建,客户端输入代码会有
#&
等特殊字符,HTTP使用POST方式进行传输时会对特殊字符进行重新编码,所以在服务器中得到用户代码时必须要先对代码进行urldecode,否则是绝对无法通过编译的。编译的具体文件是需要根据不同时间的每个用户来进行区分的,对于每次编译来说,要完成整个过程,就必不可少的创建六类文件,源码文件,标准输入文件,编译错误文件,生成的可执行文件,标准输出文件,标准错误文件。
解析用户输入
static void cutString(const std::string& input_str, std::vector<std::string>& output_vec, std::string cut_str) {
boost::split(output_vec, input_str, boost::is_any_of(cut_str), boost::token_compress_off);
}
static void codeToKv(const std::string& user_body, Json::Value& req) {
// 先对用户输入的body进行&切分
std::vector<std::string> tmp_vec;
std::unordered_map<std::string, std::string> code_kv;
boostUtil::cutString(user_body, tmp_vec, "&");
// 再进行urldecode解析,对等号进行切分
for(const auto & k : tmp_vec) {
std::vector<std::string> code_vec;
boostUtil::cutString(k, code_vec, "=");
code_kv[code_vec[0]] = urlCodeParse::urlDecode(code_vec[1]);
}
//将结果写入json
for(auto v : code_kv) {
req[v.first] = v.second;
}
}
- urldecode之后将用户输入代码构造成Json字符串,交给编译模块来进行编译运行,编译之前服务器会对不同的输入创建一个唯一的标示,用来区分不同的用户输入,编译使用子进程程序替换,父进程阻塞等待子进程完成编译,编译完成之后使用
stat
来判断是否生成可执行文件进一步判断编译是否成功,同时将编译过程中的错误信息记录到编译错误文件中
编译过程
static bool compile(std::string fileName) {
char *cmd[20] = {
0};
char space[20][50] = {
{
0}};
for(int i = 0; i < 20; i++) {
cmd[i] = space[i];
}
sprintf(cmd[0], "%s", "g++");
sprintf(cmd[1], "%s", srcFilePath(fileName).c_str());
sprintf(cmd[2], "%s", "-o");
sprintf(cmd[3], "%s", exeFilePath(fileName).c_str());
sprintf(cmd[4], "%s", "-std=c++11");
cmd[5] = NULL;