搭建OJ系统

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了搭建OJ系统相关的知识,希望对你有一定的参考价值。

参考技术A [toc]

本文介绍如何利用开源OJ系统源码快速搭建OJ系统

开源青岛大学oj的搭建(傻瓜式操作) : Uncle_drew

docker-compose up解决错误ERROR: Couldn't connect to Docker daemon at http+docker://localunixsocket - is it running?

毕设项目:基于BS模型的在线OJ系统

系列文章目录


文章目录


前言


一、在线OJ系统描述

实现一个在线OJ系统类似于力扣或者牛客网的核心部分刷题代码练习功能,提供了用户一个可以在线刷题编写代码并且能够进行编译运行的环境,题目通过序号排序,题目也有难度等级的划分,测试用例等等。在编写代码的同时提供了语法纠错、代码高亮、自动补全等基本功能。

用户可以通过域名加上端口号访问服务器,系统内置了多道编程题,用户点击对应题目就可以进行练习,并且题目内含有大量测试样例。服务器端会根据用户编写代码会进行用例的测试,检测用户代码是否符合题意,并且可以将编译成功结果或者编译出错的原因返回给浏览器端。

二、在线编译模块



在现编译模块的实现:此模块的核心完成"在线",用户把写好的代码通过网页提交到服务器上,服务器调用g++完成编译过程,并且调用刚生成的可执行程序,验证程序结果,返回给用户提交的浏览器上。

1.搭建一个HTTP服务器完成在线编译

搭建一个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库。如果你正在寻找一个“非阻塞”库,这不是你想要的。


快速上手一个开源项目小技巧:

2.收到HTTP请求,进行数据格式转化(HTTP中body的内容转换为JSON格式的字符串)

3.compile_server.cpp浏览器提交JSON数据请求服务器,服务器调用在线编译模块编译,把结果返回给浏览器

  • Json如何从req请求中获取到Json请求?
  • 从req对象中获取到。
  • Json如何和Http协议结合?
  • 需要的请求格式是json格式,HTTP能够提供的格式,是另外一种键值对的 格式,所以要对HTTP提供的格式进行格式的转换。
  • 此处由于提交的代码中可能会包含一些特殊符号,这些特殊符号要正确传 输时,就会进行urlencode,这一步由浏览器自动完成。
  • 服务器收到请求之后的第一件事就是切分键值对,再urldecode,然后解> 析数据,整理成需要的Json格式。
  • Json如何进行解析和构造?
  • 使用jsoncpp第三方库。

jsoncpp第三方库获取办法:

     1	#include <unordered_map>
     2	
     3	#include "httplib.h"
     4	#include "compile.hpp"
     5	//#include "jsoncpp/json/json.h"
     6	
     7	#include<jsoncpp/json/json.h>
     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<std::string, std::string> 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<std::string,std::string>::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	


4.util.hpp工具类

1.TimeUtil类时间戳获取工具TimeUtil标识文件的不同

   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 ;

2.打印日志的工具

    39	
    40	// 打印日志的工具
    41	
    42	// 期望打印出的日志格式: 
    43	//      [I时间戳 util.hpp:31] hello
    44	// 日志的使用方式形如: LOG(INFO) << "hello" << "\\n";
    45	// 日志的级别: 
    46	//      FATAL   致命
    47	//      ERROR   错误
    48	//      WAENING 警告
    49	//      INFO    提示
    50	
    51	enum Level 
    52	
    53	    INFO, 
    54	    WARNING, 
    55	    ERROR,
    56	    FATAL
    57	;
    58	
    59	
    60	inline std::ostream& Log(Level level, const std::string& file_name, int line_num)
    61	
    62	    //前缀
    63	    std::string prefix = "[";
    64	
    65	    if (level == INFO)
    66	    
    67	        prefix += "I";
    68	    
    69	    else if (level == WARNING)
    70	    
    71	        prefix += "W";
    72	    
    73	    else if (level == ERROR)
    74	    
    75	        prefix += "E";
    76	    
    77	    else if (level == FATAL)
    78	    
    79	        prefix += "F";
    80	    
    81	
    82	    prefix += std::to_string(TimeUtil::TimeStamp());
    83	    prefix += " ";
    84	    prefix += file_name;
    85	    prefix += ":";
    86	    prefix += std::to_string(line_num);
    87	    prefix += "] ";
    88	
    89	    
    90	    std::cout << prefix;
    91	    return std::cout;
    92	
    93	
    94	#define LOG(level) Log(level, __FILE__, __LINE__)

3.文件类FileUtil把文件所有内容读取出来,放到content字符串中

97	/
    98	// 文件相关工具类
    99	
   100	class FileUtil 
   101	
   102	public:
   103	
   104	    //  传入一个文件路径,把文件所有内容读取出来,放到content字符串中
   105	    //  下面这个函数参数是 输出型参数
   106	    //
   107	    //  输入型参数用const引用
   108	    //  输出型参数用指针
   109	    //  输入输出型参数用引用
   110	    //  
   111	    static bool Read(const std::string& file_path, std::string* content)
   112	    
   113	        //content->clear();
   114	        (*content).clear();
   115	        std::ifstream file(file_path.c_str());
   116	        if (!file.is_open())
   117	        
   118	            return false;
   119	        
   120	
   121	        std::string line;
   122	        while (std::getline(file, line))
   123	        
   124	            *content += line + "\\n";
   125	        
   126	
   127	        file.close();
   128	        return true;
   129	    
   130	
   131	    static bool Write(const std::string& file_path, const std::string& content)
   132	    
   133	        std::ofstream file(file_path.c_str());
   134	        if (!file.is_open())
   135	        
   136	            return false;
   137	        
   138	
   139	        file.write(content.c_str(), content.size());
   140	
   141	        file.close();
   142	        return true;
   143	    
   144	;

4.URL body解析模块

首先安装boost标准库来进行字符串切分。

 146	///
   147	// URL / body解析模块
   148	
   149	// 使用boost库中的函数完成字符串某些操作
   150	class StringUtil
   151	
   152	public:
   153	    // 使用boost split进行字符串的切分
   154	    // aaa bbb ccc 按照1个 空格切分 切分成3个部分
   155	    // aaa  bbb ccc 切分成3或者4 
   156	    // is_any_of  表示多个字符切割 & =
   157	    //  split中有一个参数叫做 token_compress_off 标识是否打开还是关闭分隔符压缩就是4个,如果打开上述就会切分为3部分,token_compress_on
   158	    static void Split(const std::string& input, const std::string& split_char, std::vector<std::string>* output)
   159	    
   160	        boost::split(*output, input, boost::is_any_of(split_char), boost::token_compress_off);
   161	    
   162	;
   163	
   164	// 对url body的解析模块
   165	class UrlUtil
   166	
   167	public:
   168	    static void ParseBody(const std::string& body, std::unordered_map<std::string, std::string>* params)
   169	    
   170	        // 1.先对body字符串进行切分,切分成键值对形式
   171	        //   1.1 先按照 & 切分
   172	        //   1.2 再按照 = 切分
   173	        // 使用boost split进行切分
   174	        std::vector<std::string> kvs;
   175	        StringUtil::Split(body, "&", &kvs);
   176	        
   177	        for (size_t i = 0; i < kvs.size(); i++)
   178	           
   179	            std::vector<std::string> kv;
   180	            
   181	            // kvs[i]存的是一个键值对
   182	            StringUtil::Split(kvs[i], "=", &kv);
   183	
   184	            //kv[0]=key kv[1]=value
   185	            if (kv.size() != 2)
   186	            
   187	                continue;
   188	            
   189	
   190	            // 出参,将切分好的键值对,传给调用位置
   191	            // unordered_map [] 操作,如果key不存在则新增,如果key存在,则获取到value
   192	            // 2.对键值对中的转义过的字符进行urldecode
   193	            // 只用对value转义,key不用转义
   194	            (*params)[kv[0]] = UrlDecode(kv[1]);
   195	        
   196	    
   197	
   198	
   199	    static unsigned char ToHex(unsigned char x)   
   200	       
   201	        return  x > 9 ? x + 55 : x + 48;   
   202	      
   203	
   204	    static unsigned char FromHex(unsigned char x)   
   205	       
   206	        unsigned char y;  
   207	        if (x >= 'A' && x <= 'Z') y = x - 'A' + 10;  
   208	        else if (x >= 'a' && x <= 'z') y = x - 'a' + 10;  
   209	        else if (x >= '0' && x <= '9') y = x - '0';  
   210	        else assert(0);  
   211	        return y;  
   212	      
   213	
   214	    static std::string UrlEncode(const std::string& str)  
   215	      
   216	        std::string strTemp = "";  
   217	        size_t length = str.length();  
   218	        for (size_t i = 0; i < length; i++)  
   219	          
   220	            if (isalnum((unsigned char)str[i]) ||   
   221	                    (str[i] == '-') ||  
   222	                    (str[i] == '_') ||   
   223	                    (str[i] == '.') ||   
   224	                    (str[i] == '~'))  
   225	                strTemp += str[i];  
   226	            else if (str[i] == ' ')  
   227	                strTemp += "+";  
   228	            else  
   229	              
   230	                strTemp += '%';  
   231	                strTemp += ToHex((unsigned char)str[i] >> 4);  
   232	                strTemp += ToHex((unsigned char)str毕设项目:基于BS模型的在线OJ系统

Online Judge(OJ)搭建——4具体实现

在线OJ项目

HUSTOJ 快速稳定搭建方式 需要修改的配置 常见问题解答

CCSUOJ评测系统——第四次scrum冲刺

在线OJ系统