使用c++解析http\https数据遇到的问题(附代码)

开发环境:vs2019 

系统:win10

openssl版本:Win32OpenSSL-3_0_7

访问网页:www.baidu.com

先说明以下问题:c++在发送https请求后,接收从客户端返回的数据时时,发现有时内容夹杂一些奇怪的数据(十六进制,范围从0-8000),如下图

正常应该是

两者唯一的区别是发送的http协议一个为1.0,另一个为1.2

经过检验,十六进制52f对应的值恰好52f数据之后到8000数据之前这中间内容的字节数,这里不演示(因为自己代码可能会出现问题,所以后面用fiddle抓包来验证,使用composer,下图中HTTP/2.0那边进行调整,范围为0.9、1.0、1.1、1.2、2.0,不解码)

 0.9结果:500错误(应该是服务器那边不支持,忽略掉)

 1.0结果

1.1结果:比1.0内容前面多出了d55

 1.2结果:和1.1一致

 2.0结果:与1.1和1.2一致

 猜测:从1.1开始接收到的内容会分成一块一块,每块前面会包含这一块数据的大小,范围十六进制从0-8000,由后往前统计(简单说:[小于8000],[8000],[8000]....[8000],[0])

 其实在这里还有一种限制是返回的数据Content-Type需要为text/html,才能满足猜测,本人在代码中曾接收过Content-Type为text/plain,试验过1.0和1.1,结果都一样(因为fiddler抓到的几乎都是text/html,就不演示),之所以写猜测,其实是本人没有进行大规模实验,除了Content-Type可能还存在其它限制,感兴趣的人可以自己去尝试一下

最后上一下代码(c++http\https发送get或post请求),代码本质来源于网上各个地方,在此是将代码进行修改、统合。(该代码是本人毕业设计的部分内容,非全部,注释可能偏少或不规范,需要的人自行复制粘贴使用,代码已自测过)

irg3_log.hpp(主要是用来打印日志)



#ifndef _IRG3_LOG_HPP_
#define _IRG3_LOG_HPP_
#include <thread>
#include <fstream>
#include "irg3_utils.hpp"
#include <mutex>

#define _DEBUG_TRACE_PRINT_ 2
#if 0 != _DEBUG_TRACE_PRINT_
#include <cstdio>
#include <thread>
#include <string>
#endif




//三个需要配合使用,否则会出问题
//获取运行时间
#define IRG3_BEGIN_CLOCK clock_t start_clock_ = clock()
#define IRG3_END_CLOCK clock_t end_clock_ = clock()
#define IRG3_GET_CLOCK static_cast<double>(end_clock_ - start_clock_) / CLOCKS_PER_SEC


class Irg3_Log {
private:
	Irg3_Log() {}
public:
	virtual ~Irg3_Log() {
		if (out) {
			out.close();
		}
	}

	void Init(const char* path_ = "irg3.log") {
		out.open(path_);
		if (!out) {
			return;
		}
	}
	void UnInit() {
		Close();
	}
	void Close() {
		if (out) {
			out.close();
		}
	}
	static Irg3_Log* getInstance() {
		static Irg3_Log log;
		return &log;
	}
	void write(const char* str_) {
		if (!out) {
			return;
		}
		//1000行后从头开始写入
		static int num_ = 0;

		char buf_[64] = "";
		time_t t = time(NULL); tm* _tm = localtime(&t);
		sprintf(buf_, "%04d-%02d-%02d %02d:%02d:%02d %5d ", _tm->tm_year + 1900, _tm->tm_mon + 1, _tm->tm_mday, \
			_tm->tm_hour, _tm->tm_min, _tm->tm_sec, std::this_thread::get_id());
		my_mutex_.lock();
		if (num_ == 1000) {
			num_ = 0;
			out.seekp(0);
		}
		num_++;
		out << buf_;
		out << irg3_utils_s_to_utf8(str_);
		out << std::endl;
		my_mutex_.unlock();
	}
private:
	std::ofstream out;
	std::mutex my_mutex_;
};


#define IRG3_LOG_ERROR(fmt,...) {std::string file_ = __FILE__;char buf_[1024] = "";\
		sprintf(buf_,"[ERROR] %s(%d): "##fmt,file_.substr(file_.rfind('\\')+1).c_str(),\
        __LINE__, ##__VA_ARGS__);Irg3_Log::getInstance()->write(buf_);}

#define IRG3_LOG_INFO(fmt,...) {std::string file_ = __FILE__;char buf_[1024] = "";\
		sprintf(buf_,"[INFO] %s(%d): "##fmt,file_.substr(file_.rfind('\\')+1).c_str(),\
        __LINE__, ##__VA_ARGS__);Irg3_Log::getInstance()->write(buf_);}

#define IRG3_LOG_WARN(fmt,...) {std::string file_ = __FILE__;char buf_[1024] = "";\
		sprintf(buf_,"[WARN] %s(%d): "##fmt,file_.substr(file_.rfind('\\')+1).c_str(),\
        __LINE__, ##__VA_ARGS__);Irg3_Log::getInstance()->write(buf_);}





#endif // !_IRG3_LOG_HPP_

irg3_report.hpp(用来生成测试报告)





#ifndef _IRG3_REPORT_HPP_
#define _IRG3_REPORT_HPP_
#include <thread>
#include <fstream>
#include "irg3_utils.hpp"
#include "irg3_log.hpp"

#define IRG3_REPORT_LOG_FILE_PATH "report\\irg3_report.log"
#define IRG3_REPORT_HTML_FILE_PATH "report\\irg3_report.html"

//==========================================================================
//                              LOG
//==========================================================================
class Irg3_ReportLog {
private:
    Irg3_ReportLog() {}
public:
    virtual ~Irg3_ReportLog() {}
    void Init() {
        out.open(IRG3_REPORT_LOG_FILE_PATH);
    }
    void UnInit() {
        out.close();
    }

    static Irg3_ReportLog* getInstance() {
        static Irg3_ReportLog log;
        return &log;
    }
    void report(const char* str_) {
        if (!out) {
            return;
        }
        char buf_[64] = "";
        time_t t = time(NULL); tm* _tm = localtime(&t);
        sprintf(buf_, "%04d-%02d-%02d %02d:%02d:%02d %5d ", _tm->tm_year + 1900, _tm->tm_mon + 1, _tm->tm_mday, \
            _tm->tm_hour, _tm->tm_min, _tm->tm_sec, std::this_thread::get_id());

        my_mutex_.lock();
        out << buf_;
        out << irg3_utils_s_to_utf8(str_);
        out << std::endl;
        my_mutex_.unlock();
    }
    int get_cur_position() {
        return out.tellp();
    }
    //纯文本
    void report_text(const char* str_) {
        if (!out) {
            return;
        }
        out << irg3_utils_s_to_utf8(str_);
    }
    void lock() {
        my_mutex_.lock();
    }
    void unlock() {
        my_mutex_.unlock();
    }

private:
    std::ofstream out;
    std::mutex my_mutex_;
};


#define IRG3_REPORT_INIT Irg3_ReportLog::getInstance()->Init();
#define IRG3_REPORT_UNINIT Irg3_ReportLog::getInstance()->UnInit();

#define IRG3_REPORT_ERROR(fmt,...) {std::string file_ = __FILE__;char irg3_report_buf_[1024] = "";\
		sprintf(irg3_report_buf_,"[ERROR] %s(%d): "##fmt,file_.substr(file_.rfind('\\')+1).c_str(),\
        __LINE__, ##__VA_ARGS__);Irg3_ReportLog::getInstance()->report(irg3_report_buf_);}

#define IRG3_REPORT_INFO(fmt,...) {std::string file_ = __FILE__;char irg3_report_buf_[1024] = "";\
		sprintf(irg3_report_buf_,"[INFO] %s(%d): "##fmt,file_.substr(file_.rfind('\\')+1).c_str(),\
        __LINE__, ##__VA_ARGS__);Irg3_ReportLog::getInstance()->report(irg3_report_buf_);}

#define IRG3_REPORT_WARN(fmt,...) {std::string file_ = __FILE__;char irg3_report_buf_[1024] = "";\
		sprintf(irg3_report_buf_,"[WARN] %s(%d): "##fmt,file_.substr(file_.rfind('\\')+1).c_str(),\
        __LINE__, ##__VA_ARGS__);Irg3_ReportLog::getInstance()->report(irg3_report_buf_);}

//自定义格式,但最大不超过1024
#define IRG3_REPORT_TEXT(fmt,...) {char irg3_report_buf_[1024] = "";\
		sprintf(irg3_report_buf_,##fmt,##__VA_ARGS__);Irg3_ReportLog::getInstance()->report_text(irg3_report_buf_);}

//不提供定义格式,专门传输数据量大的
#define IRG3_REPORT_TEXT_EX(fmt) Irg3_ReportLog::getInstance()->report_text(fmt)
#define IRG3_REPORT_TEXT_LOCK Irg3_ReportLog::getInstance()->lock()
#define IRG3_REPORT_TEXT_UNLOCK Irg3_ReportLog::getInstance()->unlock()

#define IRG3_REPORT_CUR_POSITION Irg3_ReportLog::getInstance()->get_cur_position()


//==========================================================================
//                              HTML
//==========================================================================
typedef struct Irg3_Report_File {
    fpos_t beg_position_ = -1;
    fpos_t end_position_ = -1;
    int pass_ = 0;
    int fail_ = 0;
    int error_ = 0;
    int skip_ = 0;
}irg3_report_file;

typedef struct Irg3_Report_Node {
    std::string name_;  //文件名

    std::vector<irg3_report_file> vecs_; //执行次数

}irg3_report_node;

typedef struct Irg3_Report_Script {
    std::string type_;  //脚本类型
    std::string content_;
    std::vector<irg3_report_node> vecs_;
}irg3_report_script;

typedef struct Irg3_Report_Message {
    std::string beg_time_;
    std::string end_time_;
    std::string time_;
}irg3_report_message;

class Irg3_ReportHtml {
private:
    Irg3_ReportHtml() {};

    typedef struct Node {
        int pass_ = 0;  //通过数量
        int fail_ = 0;  //失败数量
        int error_ = 0; //错误数量
        int skip_ = 0;  //跳过数量
        int scripts_ = 0;   //脚本数量
        int all_ = 0;
    }inline_node;

public:
    virtual ~Irg3_ReportHtml() {};
    static Irg3_ReportHtml* getInstance() {
        static Irg3_ReportHtml report_;
        return &report_;
    }

    bool report(irg3_report_message& mes_, std::vector<irg3_report_script>& vecs_) {
        out.open(IRG3_REPORT_HTML_FILE_PATH);
        if (!out) {
            return false;
        }

        report_report(out, "IRG3测试报告", vecs_, mes_);
        out.close();
        return true;
    }
private:
    std::ofstream out;

private:

    //内容
    void report_content(std::ofstream& out, std::string& name_, int id_, irg3_report_file& file_) {
        char temp[64] = "";
        sprintf(temp, "%s.%d", name_.c_str(), id_);
        std::string str_ = temp;
        out << "<tr id='div_" << str_ << "' name='div_" << name_ << "' class=\"hiddenRow\">";
        out << "<td>" << str_ << "</td>";
        out << "<td colspan='7' class='errorCase'><div class='popup_retry_window'>";
        out << "<span class=\"lang-cn\">" << irg3_utils_s_to_utf8(" 第 ") << id_ << irg3_utils_s_to_utf8(" 次循环 ") << "</span>";
        out << "<span class=\"lang-en\"> Try 1 </span></div></td>";
        out << "<td class='errorCase' align='center'><a class=\"popup_link\" onfocus='this.blur();'href=\"javascript:showTestDetail('div_" << str_ << "',0, true)\">";
        out << "<span class=\"lang-cn\">" << irg3_utils_s_to_utf8("细节") << "</span>";
        out << "<span class=\"lang-en\">error</span></a></td></tr>";


        out << "<tr id='div_S_" << str_ << "' name='div_" << str_ << "' class=\"hiddenRow\"><td colspan='10'><div class=\"popup_window\">";
        out << "<div style='text-align: right; color:red;cursor:pointer'onclick=\"document.getElementById('div_S_" << str_ << "').className = 'hiddenRow'\"><a onfocus='this.blur();'>[x]</a></div><pre>";
        //具体内容

        FILE* fp = nullptr;
        fp = fopen(IRG3_REPORT_LOG_FILE_PATH, "rb");
        if (fp == nullptr) {
            IRG3_LOG_ERROR("打开报告日志失败");
        }
        else {
            char buf_[1025];
            fseek(fp, file_.beg_position_, SEEK_SET);
            fpos_t position;
            fgetpos(fp, &position);
            while (position < file_.end_position_) {
                fread(buf_, sizeof(char), min((sizeof(buf_) - 1), file_.end_position_ - position), fp);
                out << irg3_utils_s_to_utf8(irg3_utils_utf8_to_gb(buf_)) << std::endl;
                fgetpos(fp, &position);
            }
        }
        if (fp) {
            fclose(fp);
        }


        out << "";
        out << "</pre><div><ul class='figure_ul'><ul></div></div></td></tr>";
    }
    //脚本
    void report_script(std::ofstream& out, irg3_report_node& node_, std::string name_) {
        out << "<tr id='" << name_ << "' class=\"hiddenRow\">";
        out << "<td>" << name_ << "</td>";
        out << "<td class='errorCase' colspan='7'><div class='testcase'>" << irg3_utils_s_to_utf8(node_.name_) << "</div></td>";
        out << "<td class='errorCase'><div class='testcase' style=\"margin-left: auto;\">" << node_.vecs_.size() << "</div></td>";
        out << "<td class='errorCase' align='center'><a class=\"popup_link\" onfocus='this.blur();' href=\"javascript:showTestDetail('div_" << name_ << "'," << node_.vecs_.size() << ", false)\">";
        out << "<span class=\"lang-cn\">" << irg3_utils_s_to_utf8("细节") << "</span>";
        out << "<span class=\"lang-en\">error</span></a></td></tr>";

        for (int i = 0; i < node_.vecs_.size(); i++) {

            report_content(out, name_, i + 1, node_.vecs_[i]);
        }
    }
    //类型
    void report_type(std::ofstream& out, irg3_report_script& node) {

        int pass_ = 0;
        int fail_ = 0;
        int error_ = 0;
        int skip_ = 0;
        for (auto& it : node.vecs_) {
            for (auto& t : it.vecs_) {
                pass_ += t.pass_;
                fail_ += t.fail_;
                error_ += t.error_;
                skip_ += t.skip_;
            }
        }

        int all_ = pass_ + fail_ + skip_ + error_;
        char statistics_[8] = "";
        if (all_ == 0)
            sprintf(statistics_, "0.00%%");
        else
            sprintf(statistics_, "%.2lf%%", (pass_ * 1.0 / all_));


        out << "<tr class='errorClass'>";
        out << "<td>" << node.type_ << "</td>";
        out << "<td>" << irg3_utils_s_to_utf8(node.content_) << "</td>";
        out << "<td>" << node.vecs_.size() << "</td>";
        out << "<td class=\"passCase\">" << pass_ << "</td>";
        out << "<td class=\"failCase\">" << fail_ << "</td>";
        out << "<td class=\"errorCase\">" << error_ << "</td>";
        out << "<td class=\"skipCase\">" << skip_ << "</td>";
        out << "<td style=\"text-align:right;\">" << statistics_ << "</td>";
        out << "<td>" << all_ << "</td>";
        out << "<td><a href=\"javascript:showClassDetail('" << node.type_ << "'," << node.vecs_.size() << ")\">";
        out << "<span class=\"lang-cn\">" << irg3_utils_s_to_utf8("细节") << "</span><span class=\"lang-en\">Detail</span>";
        out << "</a></td></tr>";
        for (int i = 0; i < node.vecs_.size(); i++) {
            char temp[64] = "";
            sprintf(temp, "%s.%d", node.type_.c_str(), i + 1);
            std::string str_ = temp;
            report_script(out, node.vecs_[i], str_);
        }
    }

    void report_report(std::ofstream& out, const char* title, std::vector<irg3_report_script>& vecs_, irg3_report_message& mes_) {
        int pass_ = 0;  //通过数量
        int fail_ = 0;  //失败数量
        int error_ = 0; //错误数量
        int skip_ = 0;  //跳过数量
        int scripts_ = 0;   //脚本数量

        for (auto& it : vecs_) {
            scripts_ += it.vecs_.size();
            for (auto& ir : it.vecs_) {
                for (auto& t : ir.vecs_) {
                    pass_ += t.pass_;
                    fail_ += t.fail_;
                    error_ += t.error_;
                    skip_ += t.skip_;
                }
            }
        }

        int all_ = pass_ + fail_ + skip_ + error_;
        char statistics_[8] = "";
        sprintf(statistics_, "%.2lf%%", (pass_ * 1.0 / all_));



        //head
        {
            out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
            out << "<!DOCTYPE html PUBLIC \" -//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">";
            out << "<html xmlns=\"http://www.w3.org/1999/xhtml\">";
            out << "<head><title>" << irg3_utils_s_to_utf8(title) << "</title>";
            out << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />";
            out << "<style type=\"text/css\" media=\"screen\">";
            out << "body {font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"PingFang SC\", \"Hiragino Sans GB\", \"Microsoft YaHei\", \"Helvetica Neue\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\";font-size: 14px;}";
            out << "pre {word-wrap: break-word;word-break: break-all;overflow: auto;white-space: pre-wrap}";
            out << "h1 {font-size: 16pt;color: gray}";
            out << ".heading {margin-top: 0;margin-bottom: 1ex}";
            out << ".heading .attribute {margin-top: 1ex;margin-bottom: 0}";
            out << ".heading .description {margin-top: 4ex;margin-bottom: 6ex}";
            out << "a.popup_link:hover {color: red}";
            out << ".popup_window {display: block;position: relative;left: 0;top: 0;padding: 10px;background-color: #E6E6D6;text-align: left;font-size: 13px}";
            out << ".popup_retry_window {padding-left: 50px;}";
            out << "#show_detail_line {margin-top: 3ex;margin-bottom: 1ex}";
            out << "#result_table {width: 100%;border-collapse: collapse;border: 1px solid #777}";
            out << "#header_row {color: #fff;background-color: #777}";
            out << "#result_table td {border: 1px solid #777;padding: 2px;}";
            out << "#result_table td:nth-child(n+2) {min-width: 70px;width: 100%}";
            out << "#result_table td:nth-child(n+3) {text-align: center;}";
            out << "#result_table td:first-of-type {text-align: center;min-width: 60px;}";
            out << "#total_row {font-weight: bold}";
            out << ".passClass,.failClass,.errorClass,.skipClass {font-weight: bold}";
            out << ".passCase {background-color: #d0e9c6}";
            out << ".failCase {background-color: #ebcccc}";
            out << ".errorCase {background-color: #faf2cc}";
            out << ".skipCase {background-color: #c4e3f3}";
            out << ".hiddenRow {display: none}";
            out << ".testcase {margin-left: 2em}";
            out << "#popup {position: fixed;left: 0;top: 0;width: 100%;height: 100%;text-align: center;display: none}";
            out << "#popup .bg {background-color: rgba(0, 0, 0, .5);width: 100%;height: 100%}";
            out << "#popup img {max-width: 100%;max-height: 100%;margin: auto;position: absolute;top: 0;left: 0;bottom: 0;right: 0;}";
            out << "img.pic {cursor: pointer;width: auto;height: auto;max-width: 100%;max-height: 100%;}";
            out << "#wrapper {margin: 0 auto;border-top: solid 2px #666;}";
            out << "#wrapper .lang-en {display: none;}";
            out << "#wrapper.lang-cn p.lang-cn {display: block;}";
            out << "#wrapper.lang-cn span.lang-cn {display: inline;}";
            out << "#wrapper.lang-cn .lang-en {display: none;}";
            out << "#wrapper.lang-en .lang-cn {display: none;}";
            out << "#wrapper.lang-en p.lang-en {display: block;}";
            out << "#wrapper.lang-en span.lang-en {display: inline;}";
            out << "#lang ul {float: right;margin: 0;padding: 2px 10px 4px 10px;background: #666;border-radius: 0 0 4px 4px;list-style: none;}";
            out << "#lang li {margin: 0;float: left;padding: 0 5px;}";
            out << "#lang li a {display: block;width: 24px;height: 24px;text-indent: -4em;overflow: hidden;background: transparent url(\"data:image/png; base64, iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA + f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAHiSURBVHja1Ja/jtNAEMZ/Y2/icBdxXAMSEu/A1dBR0NJQ8CS8AQ0tb4CQgEegPgQFOh7ixJUX4vgSx96ZoUgOO3 + KRDgFX7Or0Wg + f7PzeVfcnUMi4cA4OIEAARgAvY5r10AZgOGvl69Gkm4Xk9w3fJTg9f4MDz9 + OA3AsSTC4OmThaQE3Bp9w + eRmy + hie2I8us3gOMABFNFkjlW5PTPIvOLAO7YVMjfC/Sd4YuK4nOGuyMiABv7v6pP7mKmACEAeK1YPuPoWU52FgkPUiaf + ngFDjCD + Q/Fproo1vrSbUPuvR4eF7kBwDRi4ynlzxkyUMrvKTZabbrPFb9Jd2qPh7BK4DGiRYFeTJmdC8nAsVKaUes72eOK6Xm2G0GaYhpXCTzPsXEBgOZN8unrktHbAddvAKrdCESwqmoItI74eILlkw0bjt4Zltdg + 5hL8NhSYLGmurrCxuPN7Mv951 + LAh1kLQWxBlUw68bDGtEqaStQiB0SRMWlbh1yXWPu + MIc/wzTiC0dslBQR0TArfWPwJdr21KyttLKaeJijvmaD0gTMF/z57pPt8W37E1xaylwU0iE5OhON2fgjreMVmuMXC/ntus7QYAT4BFwr + Piv4HL2xstu21Xh4jAXP77V8WfAQAixA0rudAk0AAAAABJRU5ErkJggg == \") no-repeat 50% 50%;}";
            out << "#lang li a#lang-en {background-image: url(\"data:image/png; base64, iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA + f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAIWSURBVHja1JY/SBthGIefu1xqS6K20KFDy0kopUiHmphIByUZotRAIZOTWZzFpYtbB0uh6KJTIdQhi9pBSwmmCOpgoUSKFItTh4AU6tCr16Rn5P58XZocDrlYuAz9wfHAcbzv9/2 + 932/k4QQdFIyHVbHE0iAAlwFgj7HNoG6AoRzudc/A4F/28yL2l7bb269yd9QgJAsS8zMjFIufyWRuHspXqtbnsHrH8oAIQlQJyfzlaGhCNFohJ2dI1Kp/iZ3d49IJvsvvJckmJ197JlACIEsy30KgGUJBgcjFIufSacfsLnpza2tL/x4 + qx15fR0Uz84hL8HjG1blEqHJJP9bGx8IpMZ8CSAMIzWq1cUhO24CSzLYWTkPisrH8lm46yuenN9fZ + br156WmRZFgQLjR3YrK2VyWSiFAp7TEw88iTAyZNca4t6e6h/P3EbzTRtxscfks9vk83G27JaPcOuVls/v6o4pltlajo9L1KpebG8vC9isbm2jMXmRDsZhiEAVWn4NTU1ysJCkenpMRYXS55cWnrPcSThUUVhzrquNEeFOjz8vOI4CrXa + aU7 + d3p29YJusMYwQD3Drb7AFRd14Xf0nXdtehbfAxdkhG13/5M0HCImiTcPhC2BVIAHMefOWrbCNxYqqZpvlukaVrTIrNye4CK1JH7xpSAXuAOcN3n4KfAceNG62qch4 + ygHPpv/+r + DMAXV79BpyNnBoAAAAASUVORK5CYII=\");}";
            out << ".figure_ul {text-align: center;padding: 0;}";
            out << ".figure_li {width: 30em;list-style: none;display: inline-block;vertical-align: baseline;}";
            out << "tr {height: 2em;}</style>";
        }

        //script
        {
            out << "<script language=\"javascript\" type=\"text/javascript\">";
            out << "var chartData_cn = [[" << pass_ << ", \"#1c965b\", \"" << irg3_utils_s_to_utf8("通过") << "\"],[" << fail_ << ", \"#ff5722\", \"" << irg3_utils_s_to_utf8("失败") << "\"],[" << error_ << ", \"#ff9800\", \"" << irg3_utils_s_to_utf8("错误") << "\"],[" << skip_ << ", \"#64b5f6\", \"" << irg3_utils_s_to_utf8("跳过") << "\"]];";
            out << "var chartData_en = [[" << pass_ << ", \"#1c965b\", \"Passed\"],[" << fail_ << ", \"#ff5722\", \"Failed\"],[" << error_ << ", \"#ff9800\", \"Error\"],[" << skip_ << ", \"#64b5f6\", \"Skipped\"]];";
            out << "function addClass(e, c) {if (!isClass(e, c)) {if (e.className) {e.className = e.className + \" \" + c;} else {e.className = c;}}}";
            out << "function delClass(e, c) {if (isClass(e, c)) {let r = new RegExp('(?:^|\\s)' + c + '(?!\\S)', 'g');e.className = e.className.replace(r, '');}}";
            out << "function isClass(e, c) {let r = new RegExp('(?:^|\\s)' + c + '(?!\\S)');return e.className.match(r);}";
            out << "function showCase(level) {let trs = document.getElementsByTagName(\"tr\");for (let i = 0; i < trs.length; i++) {let tr = trs[i];let id = tr.id;if (id.substr(0, 2) === \"st\") {if (level === 4 || level === 3) {delClass(tr, 'hiddenRow');} else {addClass(tr, 'hiddenRow');}}if (id.substr(0, 2) === \"ft\") {if (level === 4 || level === 2) {delClass(tr, 'hiddenRow');} else {addClass(tr, 'hiddenRow');}}if (id.substr(0, 2) === \"pt\") {if (level === 4 || level === 1) {delClass(tr, 'hiddenRow');} else {addClass(tr, 'hiddenRow');}}if (id.substr(0, 2) === \"et\") {if (level === 4 || level === 5 || level === 2) {delClass(tr, 'hiddenRow');} else {addClass(tr, 'hiddenRow');}}if (id.substr(0, 4) === \"div_\") {addClass(tr, 'hiddenRow');}}}";
            out << "function showClassDetail(cid, count) {let id_list = Array(count);let toHide = 1;for (let i = 0; i < count; i++) {let tid = cid + \".\" + (i + 1);let tr = document.getElementById(tid);if (tr) {id_list[i] = tid;if (tr.className) {toHide = 0;}}}for (let i = 0; i < count; i++) {let tid = id_list[i];if (toHide) {addClass(document.getElementById(tid), 'hiddenRow');} else {delClass(document.getElementById(tid), 'hiddenRow');}}let trs = document.getElementsByTagName(\"tr\");for (let i = 0; i < trs.length; i++) {let tr = trs[i];let id = tr.id;if (id.substr(0, 4) === \"div_\") {addClass(tr, 'hiddenRow');}}}";
            out << "function showTestDetail(div_id, count, b) {let details_div_s = document.getElementsByName(div_id);for (let j = 0; j < details_div_s.length; j++) {let details_div = details_div_s[j];if (isClass(details_div, 'hiddenRow')) {delClass(details_div, 'hiddenRow');} else {addClass(details_div, \"hiddenRow\");}}for (let i = 1; i <= count; i++) {let details_div_s = document.getElementsByName(div_id + '.' + i);for (let j = 0; j < details_div_s.length; j++) {let details_div = details_div_s[j];if (details_div !== undefined) {if (b && isClass(details_div, 'hiddenRow')) {delClass(details_div, 'hiddenRow');} else {addClass(details_div, \"hiddenRow\");}}}}}";
            out << "function html_escape(s) {s = s.replace(/&/g, \"&amp;\");s = s.replace(/</g, \"&lt;\");s = s.replace(/>/g, \"&gt;\");return s;}";
            out << "function initChart() {cMargin = 20;cSpace = 40;canvas.width = canvas.parentNode.getAttribute(\"width\") * 2;canvas.height = canvas.parentNode.getAttribute(\"height\") * 2;canvas.style.height = canvas.height / 2 + \"px\";canvas.style.width = canvas.width / 2 + \"px\";cHeight = canvas.height - cMargin * 2;cWidth = canvas.width - cMargin * 2;radius = cHeight * 2 / 6; ox = canvas.width / 2 + cSpace; oy = canvas.height / 2;tWidth = 60; tHeight = 20;posX = cMargin;posY = cMargin; textX = posX + tWidth + 15;textY = posY + 18;startAngle = endAngle = 90 * Math.PI / 180; rotateAngle = 0;totleNb = 0;new_data_arr = [];for (var i = 0; i < dataArr.length; i++) {totleNb += dataArr[i][0];}for (var i = 0; i < dataArr.length; i++) {new_data_arr.push(dataArr[i][0] / totleNb);}totalYNomber = 10;ctr = 1;numctr = 50;speed = 1.2;lineStartAngle = -startAngle;line = 40;textPadding = 10;textMoveDis = 200;}";
            out << "function goChart(dataArr) {var canvas, ctx;var cWidth, cHeight, cMargin, cSpace;var radius, ox, oy;var tWidth, tHeight;var posX, posY, textX, textY;var startAngle, endAngle;var totleNb;var ctr, numctr, speed;var mousePosition = {};var lineStartAngle, line, textPadding, textMoveDis;canvas = document.getElementById(\"chart\");if (canvas && canvas.getContext) {ctx = canvas.getContext(\"2d\");}initChart();function initChart() {cMargin = 20;cSpace = 40;canvas.width = canvas.parentNode.getAttribute(\"width\") * 2;canvas.height = canvas.parentNode.getAttribute(\"height\") * 2;canvas.style.height = canvas.height / 2 + \"px\";canvas.style.width = canvas.width / 2 + \"px\";cHeight = canvas.height - cMargin * 2;cWidth = canvas.width - cMargin * 2;radius = cHeight * 2 / 6; ox = canvas.width / 2 + cSpace; oy = canvas.height / 2;tWidth = 60; tHeight = 20;posX = cMargin;posY = cMargin; textX = posX + tWidth + 15;textY = posY + 18;startAngle = endAngle = 90 * Math.PI / 180; rotateAngle = 0;totleNb = 0;new_data_arr = [];for (var i = 0; i < dataArr.length; i++) {totleNb += dataArr[i][0];}for (var i = 0; i < dataArr.length; i++) {new_data_arr.push(dataArr[i][0] / totleNb);}totalYNomber = 10;ctr = 1;numctr = 50;speed = 1.2;lineStartAngle = -startAngle;line = 40;textPadding = 10;textMoveDis = 200;}drawMarkers();function drawMarkers() {ctx.textAlign = \"left\";for (var i = 0; i < dataArr.length; i++) {ctx.fillStyle = dataArr[i][1];ctx.fillRect(posX, posY + 40 * i, tWidth, tHeight);ctx.moveTo(parseInt(posX) + 0.5, parseInt(posY + 40 * i) + 0.5);ctx.font = 'normal 24px " << irg3_utils_s_to_utf8("微软雅黑") << "';  ctx.fillStyle = dataArr[i][1]; var percent = dataArr[i][2] + \"" << irg3_utils_s_to_utf8(":") << "\" + parseInt(100 * new_data_arr[i]) + \"%\";ctx.fillText(percent, parseInt(textX) + 0.5, parseInt(textY + 40 * i) + 0.5);}};pieDraw();function pieDraw(mouseMove) {for (var n = 0; n < dataArr.length; n++) {ctx.fillStyle = ctx.strokeStyle = dataArr[n][1];ctx.lineWidth = 1;var step = new_data_arr[n] * Math.PI * 2;var lineAngle = lineStartAngle + step / 2;  lineStartAngle += step;ctx.beginPath();var x0 = ox + radius * Math.cos(lineAngle),y0 = oy + radius * Math.sin(lineAngle),x1 = ox + (radius + line) * Math.cos(lineAngle),y1 = oy + (radius + line) * Math.sin(lineAngle),x2 = x1,y2 = y1,linePadding = ctx.measureText(dataArr[n][2]).width + 10; ctx.moveTo(parseInt(x0) + 0.5, parseInt(y0) + 0.5);yMove = y0 + (y1 - y0) * ctr / numctr;ctx.lineTo(parseInt(x1) + 0.5, parseInt(yMove) + 0.5);if (x1 <= x0) {x2 -= line;ctx.textAlign = \"right\";ctx.lineTo(parseInt(x2 - linePadding) + 0.5, parseInt(yMove) + 0.5);ctx.fillText(dataArr[n][2], x2 - textPadding - textMoveDis * (numctr - ctr) / numctr, y2 - textPadding);} else {x2 += line;ctx.textAlign = \"left\";ctx.lineTo(parseInt(x2 + linePadding) + 0.5, parseInt(yMove) + 0.5);ctx.fillText(dataArr[n][2], x2 + textPadding + textMoveDis * (numctr - ctr) / numctr, y2 - textPadding);}ctx.stroke();}ctx.save();ctx.translate(ox, oy);ctx.rotate((Math.PI * 2 / numctr) * ctr / 2);ctx.strokeStyle = \"rgba(0,0,0,\" + 0.5 * ctr / numctr + \")\";ctx.beginPath();ctx.arc(0, 0, (radius + 20) * ctr / numctr, 0, Math.PI * 2, false);ctx.stroke();for (var j = 0; j < dataArr.length; j++) {endAngle = endAngle + new_data_arr[j] * ctr / numctr * Math.PI * 2; ctx.beginPath();ctx.moveTo(0, 0); ctx.arc(0, 0, radius * ctr / numctr, startAngle, endAngle, false);ctx.fillStyle = dataArr[j][1];if (mouseMove && ctx.isPointInPath(mousePosition.x * 2, mousePosition.y * 2)) {ctx.globalAlpha = 0.8;}ctx.closePath();ctx.fill();ctx.globalAlpha = 1;startAngle = endAngle; if (j == dataArr.length - 1) {startAngle = endAngle = 90 * Math.PI / 180;}}ctx.restore();if (ctr < numctr) {ctr++;setTimeout(function () {ctx.clearRect(-canvas.width, -canvas.height, canvas.width * 2, canvas.height * 2);drawMarkers();pieDraw();}, speed *= 1.085);}}var mouseTimer = null;canvas.addEventListener(\"mousemove\", function (e) {e = e || window.event;if (e.offsetX || e.offsetX == 0) {mousePosition.x = e.offsetX;mousePosition.y = e.offsetY;} else if (e.layerX || e.layerX == 0) {mousePosition.x = e.layerX;mousePosition.y = e.layerY;}clearTimeout(mouseTimer);mouseTimer = setTimeout(function () {ctx.clearRect(0, 0, canvas.width, canvas.height);drawMarkers();pieDraw(true);}, 10);});}";
            out << "function load() {let el_wrapper = document.getElementById('wrapper');document.getElementById('lang-cn').onclick = function () {el_wrapper.className = 'lang-cn';goChart(chartData_cn);};document.getElementById('lang-en').onclick = function () {el_wrapper.className = 'lang-en';goChart(chartData_en);};let nav_lang = (location.hash || '').replace(/#/, '');if (nav_lang === 'cn' || nav_lang === 'en') {el_wrapper.className = 'lang-' + nav_lang;}let images = document.getElementsByClassName(\"pic\");let lens = images.length;let popup = document.getElementById(\"popup\");function show(event) {event = event || window.event;let target = document.elementFromPoint(event.clientX, event.clientY);showBig(target.src, target.title, target.alt);}for (let i = 0; i < lens; i++) {images[i].onclick = show;}popup.onclick = function () {popup.getElementsByTagName(\"img\")[0].src = \"\";popup.getElementsByTagName(\"img\")[0].title = \"\";popup.getElementsByTagName(\"img\")[0].alt = \"\";popup.style.display = \"none\";popup.style.zIndex = \"-1\";};function showBig(src, title, alt) {popup.getElementsByTagName(\"img\")[0].src = src;popup.getElementsByTagName(\"img\")[0].title = title;popup.getElementsByTagName(\"img\")[0].alt = alt;popup.style.display = \"block\";popup.style.zIndex = \"999999\";}draw();}";
            out << "function draw() {goChart(chartData_cn);}</script></head>";
        }



        //body  top
        {
            out << "<body onload=\"load()\"><div id=\"wrapper\" class=\"lang-cn\">";
            out << "<div id=\"lang\"><ul><li><a href=\"#cn\" id=\"lang-cn\" title=\"" << irg3_utils_s_to_utf8("简体中文") << "\">cn</a></li><li><a href=\"#en\" id=\"lang-en\" title=\"English\">en</a></li></ul></div>";

            out << "<div class='heading'><h1>" << irg3_utils_s_to_utf8("IRG3测试报告") << "</h1><table><tr><td style=\"width: 100%; vertical-align: top;\">";
            out << "<p class='attribute'><strong><span class=\"lang-cn\">" << irg3_utils_s_to_utf8("启动时间:") << "</span>";
            out << "<span class=\"lang-en\">Start Time:</span></strong> " << mes_.beg_time_ << "</p>";
            out << "<p class='attribute'><strong><span class=\"lang-cn\">" << irg3_utils_s_to_utf8("结束时间:") << "</span>";
            out << "<span class=\"lang-en\">End Time:</span></strong> " << mes_.end_time_ << "</p>";
            out << "<p class='attribute'><strong><span class=\"lang-cn\">" << irg3_utils_s_to_utf8("运行时长:") << "</span>";
            out << "<span class=\"lang-en\">Duration:</span></strong> " << mes_.time_ << "</p>";
            out << "<p class='attribute'><strong><span class=\"lang-cn\">" << irg3_utils_s_to_utf8("结果:") << "</span><span class=\"lang-en\">Status:</span></strong>";
            out << "<span class=\"lang-cn\">" << irg3_utils_s_to_utf8("合计:") << "</span>";
            out << "<span class=\"lang-en\">Total:</span>" << all_ << "&nbsp;&nbsp;&nbsp;&nbsp;";
            out << "<span class=\"lang-cn\">" << irg3_utils_s_to_utf8("通过:") << "</span>";
            out << "<span class=\"lang-en\">Passed:</span>" << pass_ << "&nbsp;&nbsp;&nbsp;&nbsp;";
            out << "<span class=\"lang-cn\">" << irg3_utils_s_to_utf8("失败:") << "</span>";
            out << "<span class=\"lang-en\">Failed:</span>" << fail_ << "&nbsp;&nbsp;&nbsp;&nbsp;";
            out << "<span class=\"lang-cn\">" << irg3_utils_s_to_utf8("错误:") << "</span>";
            out << "<span class=\"lang-en\">Error:</span>" << error_ << "&nbsp;&nbsp;&nbsp;&nbsp;";
            out << "<span class=\"lang-cn\">" << irg3_utils_s_to_utf8("跳过:") << "</span>";
            out << "<span class=\"lang-en\">Skipped:</span>" << skip_ << "&nbsp;&nbsp;&nbsp;&nbsp;</p><p class='description'>";
            out << irg3_utils_s_to_utf8("注:该网页的实现为网上代码,本人通过借鉴代码来进行修改,原代码中标签的作者名为:刘士");
            out << "</p></td><td><div height=\"400\" width=\"600\"><canvas id=\"chart\" style=\"border: 1px solid #A4E2F9;\"> ";
            out << irg3_utils_s_to_utf8("你的浏览器不支持HTML5 canvas");
            out << "</canvas></div></td></tr></table></div>";
        }


        //body  bottom
        {
            out << "<table id='result_table'><tr id='header_row'>";
            std::string strs_[][2] = { {"序号","NO"},{"测试组/测试用例","Test Group/Test case"},
                {"脚本数","Scripts"},{"通过","Passed"},{"失败","Failed"},{"错误","Erroneous"},
                {"跳过","Skipped"},{"统计","Statistics"},{"计数","Count"},{"查看","View"} };

            for (int i = 0; i < 10; i++) {
                out << "<th><span class=\"lang-cn\">" << irg3_utils_s_to_utf8(strs_[i][0].c_str()) << "</span><span class=\"lang-en\">" << strs_[i][1] << "</span></th>";
            }
            out << "</tr>";


            for (auto it : vecs_) {
                report_type(out, it);
            }



            out << "<tr id='total_row'><td>&nbsp;</td>";
            out << "<td><span class=\"lang-cn\">" << irg3_utils_s_to_utf8("合计") << "</span><span class=\"lang-en\">Total</span></td>";
            out << "<td>" << scripts_ << "</td>";
            out << "<td class=\"passCase\">" << pass_ << "</td>";
            out << "<td class=\"failCase\">" << fail_ << "</td>";
            out << "<td class=\"errorCase\">" << error_ << "</td>";
            out << "<td class=\"skipCase\">" << skip_ << "</td>";
            out << "<td style=\"text-align:right;\">" << statistics_ << "</td>";
            out << "<td>" << all_ << "</td>";
            out << "<td>&nbsp;</td></tr></table>";

            out << "<div id='ending'>&nbsp;</div><div id=\"popup\"><div class=\"bg\"><img src=\"\" alt=\"\" /></div></div>";
            out << "</div></body></html>";
        }


    }

};

#define IRG3_REPORT_HTML(irg3_mes_,irg3_cont_) Irg3_ReportHtml::getInstance()->report(##irg3_mes_,##irg3_cont_);




//==========================================================================
//                              宏定义
//==========================================================================





#endif // !_IRG3_REPORT_HPP_

irg3_utils.hpp(常用的工具类)



#ifndef _IRG3_UTILS_H_
#define _IRG3_UTILS_H_

#include< windows.h >
#include <tchar.h>
#include <string>
#include <regex>
#include <vector>


//===============================================================================================
//用来比较连个字符串是否相等
inline bool irg3_utils_cmp_str(const char* buf, const char* cmd) {
    if (buf == nullptr || cmd == nullptr)
        return false;
    while (*buf && *cmd) {
        if (*buf != *cmd) {
            break;
        }
        ++buf;
        ++cmd;
    }
    if (*cmd || *buf) {
        return false;
    }
    return true;
}
//===============================================================================================
//用来判断字符串是否满足正则表达式
inline bool irg3_utils_regex_match(std::string& str_, const char* pattern_str_) {
    if (pattern_str_ == nullptr) {
        return false;
    }
    std::regex pattern(pattern_str_);
    std::smatch res;
    if (std::regex_match(str_, res, pattern)) {
        return true;
    }
    return false;
}
inline bool irg3_utils_regex_match(const char* str_, const char* pattern_str_) {
    if (str_ == nullptr || pattern_str_ == nullptr) {
        return false;
    }
    std::string str_temp_ = str_;
    return irg3_utils_regex_match(str_temp_, pattern_str_);
}

//===============================================================================================
/*
* 将ipv4和端口号进行加密,生成一个十二字符的字符串
* return    指向buf_
*/
inline char* irg3_utils_decryp_ip_port(char* buf_, const char* ip_, int port_) {
    if (buf_ == nullptr || ip_ == nullptr) {
        return nullptr;
    }
    if (port_ > 65535 || port_ < 0) {
        return nullptr;
    }
    int order_arr_[] = { 2,0,5,3,1,4 };
    int val_[6];
    if (!irg3_utils_regex_match(ip_, "((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)")) {
        return nullptr;
    }
    sscanf_s(ip_, "%d.%d.%d.%d", &val_[0], &val_[1], &val_[2], &val_[3]);
    val_[4] = port_ >> 8;
    val_[5] = port_ & 0xFF;

    //交换顺序
    for (int i = 0; i < 6; i++) {
        if (val_[i] == val_[order_arr_[i]])continue;
        val_[i] = val_[i] ^ val_[order_arr_[i]];
        val_[order_arr_[i]] = val_[order_arr_[i]] ^ val_[i];
        val_[i] = val_[i] ^ val_[order_arr_[i]];
    }

    //重新生成字符串
    char* p = buf_;
    for (int i = 0; i < 6; i++) {
        *p++ = (val_[i] / 16) + 'G';
        *p++ = (val_[i] % 16) + 'C';
    }
    *p = '\0';
    return buf_;
}
/*
* 将decryp_ip_port加密后的字符串解密,获取ipv4和端口号
*/
inline bool irg3_utils_encryp_ip_port(const char* buf_, char* ip_, int* port_) {

    if (buf_ == nullptr || ip_ == nullptr || port_ == nullptr) {
        return false;
    }
    int order_arr_[] = { 2,0,5,3,1,4 };
    int val_[6];

    const char* p = buf_;
    int i = 0;
    while (*p && *(p + 1)) {
        if (i == 6) {
            break;
        }
        val_[i++] = ((*p - 'G') * 16) + (*(p + 1) - 'C');
        p += 2;
    }
    if (i != 6) {
        return false;
    }
    //交换顺序
    for (int i = 5; i >= 0; i--) {
        if (val_[i] == val_[order_arr_[i]])continue;
        val_[i] ^= val_[order_arr_[i]];
        val_[order_arr_[i]] ^= val_[i];
        val_[i] ^= val_[order_arr_[i]];
    }

    sprintf(ip_, "%d.%d.%d.%d", val_[0], val_[1], val_[2], val_[3]);
    *port_ = (val_[4] << 8) + val_[5];

    return true;
}
//===============================================================================================
/*
* 将字符串转成int类型
*/
inline bool irg3_utils_stoi(const char* buf_, int& val_) {
    val_ = 0;
    if (buf_ == nullptr) {
        return false;
    }
    const char* p = buf_;
    bool flag_ = false;
    if (*p == '-') {
        flag_ = true;
        p++;
    }
    while (*p) {
        if (*p < '0' || *p > '9') {
            break;
        }
        val_ = val_ * 10 + (*p++ - '0');
    }
    val_ = flag_ ? -val_ : val_;
    return true;;
}
//===============================================================================================
//用来比较一个字符串是否另一个字符串前缀
inline bool irg3_utils_with_str(const char* buf_, const char* start_, const char* end_) {
    if (buf_ == nullptr || (start_ == nullptr && end_ == nullptr)) {
        return false;
    }
    const char* p = buf_;
    const char* q = start_;
    if (start_ != nullptr) {
        while (*p && *q) {
            if (*p != *q) {
                break;
            }
            ++p;
            ++q;
        }
        if (*q) {
            return false;
        }
    }
    if (end_ != nullptr) {
        q = end_;
        while (*p) { ++p; }
        while (*q) { ++q; }
        while (p != buf_ && q != end_) {
            if (*p != *q) {
                break;
            }
            --p;
            --q;
        }
        if (q != end_) {
            return false;
        }
    }

    return true;
}
//===============================================================================================
//获取一个字符串长度
inline int irg3_utils_get_size(const char* str_) {
    if (str_ == nullptr) {
        return -1;
    }
    int len = 0;
    while (*str_) {
        ++len;
        ++str_;
    }
    return len;
}
//===============================================================================================
/*
* 获取文件状态
* path  文件路径
* return 0 路径不存在   1 文件夹   2 文件    -1 未知
*/
inline int irg3_utils_file_stat(const char* path) {
    struct _stat st;
    if (_stat(path, &st) == 0) {
        if (st.st_mode & S_IFDIR) {
            return 1;
        }
        else if (st.st_mode & S_IFREG) {
            return 2;
        }
        return -1;
    }
    return 0;
}
//===============================================================================================
/*
* 使用sunday算法,判断字符串s中是否包含字符串p
*/
inline int irg3_utils_contain(const char* s, int n, const char* p, int m) {
    if (s == nullptr || p == nullptr || n == -1 || m == -1) {
        return -1;
    }
    int next[256];
    for (int i = 0; i < 256; i++)
        next[i] = -1;
    for (int i = 0; i < m; i++) {
        next[(p[i] & 0xff)] = i;
    }
    int j;
    int k;
    int i = 0;
    while (i <= n - m) {
        j = i;
        k = 0;
        while (j < n && k < m && (s[j]) == (p[k]))
            j++, k++;
        if (k == m)
            return i;
        if (i + m < n)
            i += (m - next[(s[i + m] & 0xff)]);
        else
            break;
    }
    return -1;
}
//===============================================================================================
// std::string 转换为 UTF-8 编码
inline std::string irg3_utils_s_to_utf8(const std::string& str)
{
    int nwLen = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
    wchar_t* pwBuf = new wchar_t[nwLen + 1];//一定要加1,不然会出现尾巴 
    ZeroMemory(pwBuf, nwLen * 2 + 2);
    ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), pwBuf, nwLen);
    int nLen = ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
    char* pBuf = new char[nLen + 1];
    ZeroMemory(pBuf, nLen + 1);
    ::WideCharToMultiByte(CP_UTF8, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
    std::string retStr(pBuf);
    delete[]pwBuf;
    delete[]pBuf;

    pwBuf = NULL;
    pBuf = NULL;

    return retStr;
}
inline std::string irg3_utils_s_to_utf8(const std::string&& str)
{
    return irg3_utils_s_to_utf8(str);
}
//===============================================================================================
// 将utf-8转成gbk
inline std::string irg3_utils_utf8_to_gb(const char* str)
{
    std::string result;
    WCHAR* strSrc;
    LPSTR szRes;

    //获得临时变量的大小
    int i = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
    strSrc = new WCHAR[i + 1];
    MultiByteToWideChar(CP_UTF8, 0, str, -1, strSrc, i);

    //获得临时变量的大小
    i = WideCharToMultiByte(CP_ACP, 0, strSrc, -1, NULL, 0, NULL, NULL);
    szRes = new CHAR[i + 1];
    WideCharToMultiByte(CP_ACP, 0, strSrc, -1, szRes, i, NULL, NULL);

    result = szRes;
    delete[]strSrc;
    delete[]szRes;

    return result;
}


//===============================================================================================
//获取文件大小
inline size_t irg3_utils_get_file_size(char* filename)
{
    struct stat statbuf;
    if (filename == nullptr) {
        return -1;
    }
    if (stat(filename, &statbuf)) {
        return 0;
    }
    return statbuf.st_size;
}

//===============================================================================================
//比较两个文件是否相等
inline bool compareFiles(FILE* fp0, FILE* fp1)
{
    if (fp0 == nullptr || fp1 == nullptr) {
        return false;
    }
    bool result = true;

    while (result) {
        char b0[1024];
        char b1[1024];
        size_t const r0 = fread(b0, 1, sizeof(b0), fp0);
        size_t const r1 = fread(b1, 1, sizeof(b1), fp1);

        result = (r0 == r1);
        if (!r0 || !r1) break;
        if (result) result = memcmp(b0, b1, r0) == 0 ? true : false;
    }

    return result;
}

//===============================================================================================
//解析email_receiver对象的数据结构
inline void irg3_utils_parase_email_receiver(const char* buf_, std::vector<std::pair<std::string, std::string> >& vec_) {
    if (buf_ == nullptr || buf_[0] == '\0') {
        return;
    }
    char str_1[64] = "";
    char str_2[64] = "";
    int stat_ = 0;
    char* p = str_1;
    while (*buf_) {
        if (*buf_ == ':') {
            *p = '\0';
            p = str_2;
            buf_++;
            continue;
        }
        else if (*buf_ == ',') {
            *p = '\0';
            p = str_1;
            vec_.push_back({ str_1,str_2 });
            buf_++;
            continue;
        }
        *p++ = *buf_++;
    }
    if (*(buf_ - 1) != ',') {
        *p = '\0';
        vec_.push_back({ str_1,str_2 });
    }

}


//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================
//===============================================================================================


#endif // !_IRG3_UTILS_H_

irg3_http.h


#pragma once
#include <string>
#include <openssl/ssl.h>

#include<unordered_map>
#include <functional>

//http 接收数据格式
typedef struct irg3_http_recv_head {
    std::string http_;  //协议版本
    int code_;  //状态码
    std::unordered_map<std::string, std::string> map_; 
}irg3_http_recv_head_;


//http 发送数据格式
typedef struct irg3_http_send_head {
    std::string method_ = "GET";
    std::string pro_ = "https";
    int port_ = 443;
    std::string host_ = "www.baidu.com";
    std::string url_ = "/";
    std::string version_ = "HTTP/1.0";
    std::unordered_map<std::string, std::string>map_;
    const char* body_ = nullptr;    
    int body_size_ = -1;
    irg3_http_send_head() {
        map_["Connection"] = "close";
        map_["Accept"] = "*/*";
        map_["Accept-Language"] = "zh-Hans-CN, zh-Hans; q=0.8, en-US; q=0.5, en; q=0.3";
        map_["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134";

    }
}irg3_http_send_head_;



class irg3_https
{
public:
    virtual ~irg3_https(void);
    static irg3_https* getInstance();

    //发送get请求,参数照着irg3_http_send_head对象
    //message_ 接收数据时用到的缓存区,nullptr的话会自动创建一个1025大小的空间
    //message_size_ message_的大小
    //return -1 失败    0 成功    1 错误    2 跳过
    int https_get(const char* host_, const char* url_,
        char* message_ = nullptr, int message_size_ = -1);
    int http_get(const char* host_, const char* url_,
        char* message_ = nullptr, int message_size_ = -1);


    //发送post请求,参数照着irg3_http_send_head对象
    //message_ 接收数据时用到的缓存区,nullptr的话会自动创建一个1025大小的空间
    //message_size_ message_的大小
    //return -1 失败    0 成功    1 错误    2 跳过
    int https_post(const char* host_, const char* url_, const char* body_, int body_size_,
        char* message_ = nullptr, int message_size_ = -1);
    int http_post(const char* host_, const char* url_, const char* body_, int body_size_,
        char* message_ = nullptr, int message_size_ = -1);

    //method_ 对接收到的数据进行断言判断的方法
    //message_ 接收数据时用到的缓存区,nullptr的话会自动创建一个1025大小的空间
    //message_size_ message_的大小
    //return -1 失败    0 成功    1 错误    2 跳过
    int http(irg3_http_send_head_* head_, std::function<bool(const char* msg_)>method_,
        char* message_ = nullptr, int message_size_ = -1);

private:
    irg3_https(void);
private:
    // 建立TCP连接套接字
    bool tcp_conn(const char* pSvrIp, int iSvrPort, int& socket);


    //发送数据到服务端
    int https_send_to_svr(const irg3_http_send_head_* head_, std::function<bool(const char* msg_)>method_,
        char* message_ = nullptr, int message_size_ = -1);
    int http_send_to_svr(const irg3_http_send_head_* head_, std::function<bool(const char* msg_)>method_,
        char* message_ = nullptr, int message_size_ = -1);

    // 组装GET请求数据
    bool getGetReqData(const irg3_http_send_head_* head_, std::string& str_);
    // 组装POST请求数据
    bool getPostReqData(const irg3_http_send_head_* head_, std::string& str_);

    // 读取响应字符串到文件
    int readResponse(SSL* ssl, char* message_, int message_size_, std::function<bool(const char* msg_)>method_, bool pro_flag_ = false);
    int readResponse(int socketFd, char* message_, int message_size_, std::function<bool(const char* msg_)>method_, bool pro_flag_ = false);

};


irg3_http.cpp



#include "irg3_http.h"

#include <winsock.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>
#include <sstream>
#include <iostream>
#include "irg3_log.hpp"
#include "irg3_report.hpp"
#include "irg3_utils.hpp"
#include <fstream>
#include <io.h>



#pragma comment(lib,"libssl.lib")
#pragma comment(lib,"libcrypto.lib")
#pragma comment(lib,"ws2_32.lib")


//=======================================STATIC METHOD===============================================
//解析http接收到的头部
static bool getHttpHead(int socketFd, irg3_http_recv_head_* head_ = nullptr);
static bool getHttpHead(SSL* ssl, irg3_http_recv_head_* head_ = nullptr);
//解析http接收到的数据
//http1.0和1.1在接收到text/html类型的数据不一样,1.1会在数据中加入长度,最大未十六进制8000,如果不进行对应处理,会数据的显示
//经过fiddle抓包验证,从http1.1往后都需要进行额外处理
static int https_1_1(SSL* ssl);
static int http_1_1(int socket_);
//========================================STATIC=====================================================
static bool getHttpHead(int socketFd, irg3_http_recv_head_* head_) {

    char key_[64];
    char val_[128];
    int stat_ = 0;
    int readLen = 0;
    char pHeader[1] = { 0 };
    int i = 0;
    char* p = key_;

    if (head_ == nullptr) {
        //去除协议头,不保存
        IRG3_LOG_INFO("去除协议头,不保存");
        while ((readLen = recv(socketFd, pHeader, 1, 0)) == 1) {
            if (pHeader[0] == '\r' || pHeader[0] == '\n') {
                i++;
                if (i == 4) {
                    break;
                }
                continue;
            }
            i = 0;
        }
        return true;
    }

    // 响应头以\r\n\r\n 结束, 此处判断头是否传输完成
    //请求行
    while ((readLen = recv(socketFd, pHeader, 1, 0)) == 1) {
        if (pHeader[0] == '\r' || pHeader[0] == '\n') {
            i++;
            if (i == 2) {
                break;
            }
            *p = '\0';
            irg3_utils_stoi(key_, head_->code_);
            continue;
        }
        else if (pHeader[0] == ' ' && stat_ == 0) {
            *p = '\0';
            head_->http_ = key_;
            p = key_;
            stat_ = 1;
        }
        else {
            *p++ = pHeader[0];
        }
        i = 0;
    }

    p = key_;
    stat_ = 0;
    //请求头
    while ((readLen = recv(socketFd, pHeader, 1, 0)) == 1) {
        if (pHeader[0] == '\r' || pHeader[0] == '\n') {
            i++;
            stat_ = 0;
            if (i == 4) {
                break;
            }
            else if (i == 1) {
                *p = '\0';
                int k_ = 1;
                while (head_->map_.count(key_) == 1) {
                    sprintf(p, "_%d\0", k_);
                }
                head_->map_[key_] = val_;
                p = key_;
            }
            else if (i == 2) {
                p = key_;
            }
            continue;
        }
        else if (pHeader[0] == ':' && stat_ == 0) {
            *p = '\0';
            p = val_;
            stat_ = 1;
        }
        else {
            *p++ = pHeader[0];
        }
        i = 0;
    }
    IRG3_LOG_INFO("成功读取协议头");
    return true;
}

static bool getHttpHead(SSL* ssl, irg3_http_recv_head_* head_) {
    if (ssl == nullptr) {
        return false;
    }
    char key_[64];
    char val_[256];
    int stat_ = 0;
    int readLen = 0;
    char pHeader[1] = { 0 };
    int i = 0;
    char* p = key_;

    if (head_ == nullptr) {
        //去除协议头,不保存
        while ((readLen = SSL_read(ssl, pHeader, 1)) == 1) {
            if (pHeader[0] == '\r' || pHeader[0] == '\n') {
                i++;
                if (i == 4) {
                    break;
                }
                continue;
            }
            i = 0;
        }
        return true;
    }

    // 响应头以\r\n\r\n 结束, 此处判断头是否传输完成
    //请求行
    while ((readLen = SSL_read(ssl, pHeader, 1)) == 1) {
        if (pHeader[0] == '\r' || pHeader[0] == '\n') {
            i++;
            if (i == 2) {
                break;
            }
            *p = '\0';
            irg3_utils_stoi(key_, head_->code_);
            continue;
        }
        else if (pHeader[0] == ' ' && stat_ == 0) {
            *p = '\0';
            head_->http_ = key_;
            p = key_;
            stat_ = 1;
        }
        else {
            *p++ = pHeader[0];
        }
        i = 0;
    }

    p = key_;
    stat_ = 0;
    char* q = nullptr;
    //请求头
    while ((readLen = SSL_read(ssl, pHeader, 1)) == 1) {
        if (pHeader[0] == '\r' || pHeader[0] == '\n') {
            i++;

            if (i == 4) {
                break;
            }
            else if (i == 1) {
                *p = '\0';
                int k_ = 1;
                while (head_->map_.count(key_) == 1) {
                    sprintf(q, "_%d\0", k_++);
                }
                head_->map_[key_] = val_;
                p = key_;
            }
            else if (i == 2) {
                p = key_;
                stat_ = 0;
            }
            continue;
        }
        else if (pHeader[0] == ':' && stat_ == 0) {
            *p = '\0';
            q = p;
            p = val_;
            stat_ = 1;
        }
        else {
            *p++ = pHeader[0];
        }
        i = 0;
    }
    return true;
}

static int https_1_1(SSL* ssl) {
    int readLen = 0;
    char pHeader[1] = { 0 };
    int i = 0;
    int val_ = 0;
    // 响应头以\r\n结束, 此处判断头是否传输完成
    while ((readLen = SSL_read(ssl, pHeader, 1)) == 1) {
        if (pHeader[0] == '\r' || pHeader[0] == '\n') {
            i++;
            if (i == 2) {
                break;
            }
        }
        if (pHeader[0] >= '0' && pHeader[0] <= '9') {
            val_ = val_ * 16 + (pHeader[0] - '0');
        }
        else if (pHeader[0] >= 'a' && pHeader[0] <= 'f') {
            val_ = val_ * 16 + (pHeader[0] - 'a' + 10);
        }
    }
    //val_代表数据量,数据以\r\n结束,数据长度+2
    return val_ + 2;
}

static int http_1_1(int socket_) {
    int readLen = 0;
    char pHeader[1] = { 0 };
    int i = 0;
    int val_ = 0;
    // 响应头以\r\n\r\n 结束, 此处判断头是否传输完成
    while ((readLen = recv(socket_, pHeader, 1, 0)) == 1) {
        if (pHeader[0] == '\r' || pHeader[0] == '\n') {
            i++;
            if (i == 2) {
                break;
            }
        }
        if (pHeader[0] >= '0' && pHeader[0] <= '9') {
            val_ = val_ * 16 + (pHeader[0] - '0');
        }
        else if (pHeader[0] >= 'a' && pHeader[0] <= 'f') {
            val_ = val_ * 16 + (pHeader[0] - 'a' + 10);
        }
    }
    return val_ + 2;
}
//=======================================HTTPS=================================================

irg3_https::irg3_https(void) {
}

irg3_https::~irg3_https(void) {
}

irg3_https* irg3_https::getInstance()
{
    static irg3_https httpsClient;
    return &httpsClient;
}

int irg3_https::https_get(const char* host_, const char* url_, char* message_, int message_size_) {
    irg3_http_send_head_ head_;
    head_.method_ = "GET";
    head_.pro_ = "https";
    head_.port_ = 443;
    head_.host_ = host_;
    head_.url_ = url_;
    return https_send_to_svr(&head_,nullptr, message_, message_size_);
}
int irg3_https::http_get(const char* host_, const char* url_, char* message_, int message_size_) {
    irg3_http_send_head_ head_;
    head_.method_ = "GET";
    head_.pro_ = "http";
    head_.port_ = 80;
    head_.host_ = host_;
    head_.url_ = url_;
    return http_send_to_svr(&head_,nullptr, message_, message_size_);
}

int irg3_https::https_post(const char* host_, const char* url_, const char* body_, int body_size_, char* message_, int message_size_) {
    irg3_http_send_head_ head_;
    head_.method_ = "POST";
    head_.pro_ = "https";
    head_.port_ = 443;
    head_.host_ = host_;
    head_.url_ = url_;
    head_.body_ = body_;
    head_.body_size_ = body_size_;
    return https_send_to_svr(&head_,nullptr, message_, message_size_);
}

int irg3_https::http_post(const char* host_, const char* url_, const char* body_, int body_size_, char* message_, int message_size_) {
    irg3_http_send_head_ head_;
    head_.method_ = "POST";
    head_.pro_ = "http";
    head_.port_ = 80;
    head_.host_ = host_;
    head_.url_ = url_;
    head_.body_ = body_;
    head_.body_size_ = body_size_;
    return http_send_to_svr(&head_,nullptr, message_, message_size_);
}

int irg3_https::http(irg3_http_send_head_* head_, std::function<bool(const char*msg_)>method_,
    char* message_, int message_size_) {
    if (head_ == nullptr) {
        return -1;
    }
    if (head_->pro_ == "http") {
        return http_send_to_svr(head_,method_, message_, message_size_);
    }
    else if (head_->pro_ == "https") {
        return https_send_to_svr(head_,method_, message_, message_size_);
    }

}

int irg3_https::http_send_to_svr(const irg3_http_send_head_* head_, std::function<bool(const char* msg_)>method_, char* message_, int message_size_) {
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        return -1;
    }
    int stat_ = -1;
    int socketFd = 0;;
    do {
        char* pSvrIp = NULL;
        struct hostent* pHostent = NULL;
        pHostent = gethostbyname(head_->host_.c_str());
        if (pHostent == NULL) {
            IRG3_LOG_ERROR("获取域名 %s 的ip地址失败", head_->host_.c_str());
        }
        pSvrIp = inet_ntoa(*(struct in_addr*)pHostent->h_addr_list[0]);

        // 1.建立TCP连接
        if (!tcp_conn(pSvrIp, head_->port_, socketFd)) {
            IRG3_LOG_ERROR("与服务器 %s 建立连接失败", pSvrIp);
            break;
        }
        //获取数据
        std::string strReqData;
        if (head_->method_ == "GET") {
            getGetReqData(head_, strReqData);
        }
        else if (head_->method_ == "POST") {
            getPostReqData(head_, strReqData);
        }

        IRG3_LOG_INFO("开始发送数据");
        //发送数据
        if (send(socketFd, strReqData.c_str(), strReqData.size(), 0) == SOCKET_ERROR) {
            IRG3_LOG_ERROR("发送数据失败");
            break;
        }
        IRG3_LOG_INFO("结束发送数据");
        IRG3_LOG_INFO("开始接收首部数据");
         5.读取响应数据  
        irg3_http_recv_head_ recv_head_;
        //读取http协议头部
        getHttpHead(socketFd, &recv_head_);
        {
            IRG3_REPORT_INFO("接收的首部数据:")
            IRG3_REPORT_TEXT_LOCK;
            IRG3_REPORT_TEXT("%s %d\n", recv_head_.http_.c_str(), recv_head_.code_);
            for (auto& it : recv_head_.map_) {
                IRG3_REPORT_TEXT(it.first.c_str());
                IRG3_REPORT_TEXT(" = ");
                IRG3_REPORT_TEXT(it.second.c_str());
                IRG3_REPORT_TEXT("\n");
            }
            IRG3_REPORT_TEXT_UNLOCK;
        }
        std::string& content_type_ = recv_head_.map_["Content-Type"];

        //iRet = readResponse(socketFd, path_, message_, message_size_);
        //http1.1在接收到数据会分成一块一块,每块都会插入对应长度,以\r\n结尾,需要进行特殊处理
        if (irg3_utils_contain(head_->version_.c_str(), head_->version_.length(), "1.1", 3) != -1 &&
            irg3_utils_contain(content_type_.c_str(), content_type_.length(), "text/html", 9) != -1) {
            //http/1.1 返回中Content-Type: text/html会比较特别,需要额外处理
            stat_ = readResponse(socketFd, message_, message_size_, method_,true);
        }
        else {
            stat_ = readResponse(socketFd, message_, message_size_, method_);
        }
    } while (false);

    if (socket) {
        IRG3_LOG_INFO("关闭socket连接:%d", socketFd);
        closesocket(socketFd);
    }

    WSACleanup();
    return stat_;
}

int irg3_https::https_send_to_svr(const irg3_http_send_head_* head_, std::function<bool(const char* msg_)>method_, char* message_, int message_size_)
{

    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        return -1;
    }
    int stat_ = -1;
    int socketFd = 0;
    SSL_CTX* ctx = 0;
    SSL* ssl = 0;
    do
    {
        char* pSvrIp = NULL;
        struct hostent* pHostent = NULL;
        pHostent = gethostbyname(head_->host_.c_str());
        if (pHostent == NULL) {
            IRG3_LOG_ERROR("获取域名 %s 的ip地址失败", head_->host_.c_str());
            break;
        }
        pSvrIp = inet_ntoa(*(struct in_addr*)pHostent->h_addr_list[0]);

        // 1.建立TCP连接
        if (!tcp_conn(pSvrIp, head_->port_, socketFd)) {
            IRG3_LOG_ERROR("与服务器 %s 建立连接失败", pSvrIp);
            break;
        }

        // 2.SSL初始化, 关联Socket到SSL,并建立连接
        SSL_library_init();
        OpenSSL_add_all_algorithms();
        SSL_load_error_strings();
        ctx = SSL_CTX_new(SSLv23_client_method());
        if (ctx == NULL) {
            IRG3_LOG_ERROR("新建一个SSL对象失败");
            break;
        }
        ssl = SSL_new(ctx);
        SSL_set_fd(ssl, socketFd);
        int retCode = SSL_connect(ssl);
        if (retCode != 1) {
            IRG3_LOG_ERROR("与服务器 %s 建立SSL连接失败", pSvrIp);
            break;
        }

        std::string strReqData;
        if (head_->method_ == "GET") {
            getGetReqData(head_, strReqData);
        }
        else if (head_->method_ == "POST") {
            getPostReqData(head_, strReqData);
        }
        // 4.通过SSL发送数据,数据量不大一次write就够了,如果是文件上传就需要循环写了。
        if (head_->method_ == "POST"&&head_->body_[0] == 'i' && head_->body_[1] == '\0') {
            //发送前面的数据
            SSL_write(ssl, strReqData.c_str(), strReqData.length());

            if (irg3_utils_file_stat((head_->body_ + 2)) != 2) {
                break;
            }
            // 使用 "rb" 读取二进制方式打开文件
            FILE* p = fopen((head_->body_ + 2), "rb");
            IRG3_LOG_INFO("打开数据文件:%s", (head_->body_ + 2));
            if (p == nullptr) {
                IRG3_LOG_ERROR("指定的文件路径不存在:%s",(head_->body_ + 2));
                break;
            }
            // 用于接收读取数据的缓冲区
            long fileLen = filelength(fileno(p));

            //发送body的长度
            char buffer[1024] = { 0 };
            sprintf(buffer,"Content-Length: %d\r\n\r\n\0", fileLen);
            IRG3_LOG_INFO("开始发送数据");
            SSL_write(ssl, buffer, irg3_utils_get_size(buffer));

            int len = 0;
            while ((len = fread(buffer, sizeof(char), sizeof(buffer), p)) > 0) {
                SSL_write(ssl, buffer, sizeof(buffer));
            }
            //发送结尾
            SSL_write(ssl, "\r\n\r\n", 4);
            IRG3_LOG_INFO("结束发送数据");
            IRG3_LOG_INFO("关闭数据文件:%s", (head_->body_ + 2));
            fclose(p);
        }
        else {
            IRG3_LOG_INFO("开始发送数据");
            int writeLen = SSL_write(ssl, strReqData.c_str(), strReqData.length());

            if (writeLen <= 0) {
                IRG3_LOG_ERROR("发送失败");
                break;
            }
            IRG3_LOG_INFO("结束发送数据");
        }
        
        irg3_http_recv_head_ recv_head_;
        getHttpHead(ssl, &recv_head_);
        {
            IRG3_REPORT_INFO("接收的首部数据:")
            IRG3_REPORT_TEXT_LOCK;
            IRG3_REPORT_TEXT("%s %d\n",recv_head_.http_.c_str(),recv_head_.code_);
            for (auto& it : recv_head_.map_) {
                IRG3_REPORT_TEXT(it.first.c_str());
                IRG3_REPORT_TEXT(" = ");
                IRG3_REPORT_TEXT(it.second.c_str());
                IRG3_REPORT_TEXT("\n");
            }
            IRG3_REPORT_TEXT_UNLOCK;
        }

        std::string &content_type_ = recv_head_.map_["Content-Type"];
        //http1.1在接收到数据会分成一块一块,每块都会插入对应长度,以\r\n结尾,需要进行特殊处理
        if (irg3_utils_contain(head_->version_.c_str(), head_->version_.length(), "1.1", 3) != -1&&
            irg3_utils_contain(content_type_.c_str(), content_type_.length(),"text/html",9)!=-1) {
            //http/1.1 返回中Content-Type: text/html会比较特别,需要额外处理
            stat_ = readResponse(ssl, message_, message_size_, method_, true);
        }
        else {
            stat_ = readResponse(ssl, message_, message_size_, method_);
        }

    } while (false);

    // 6.关闭socket、断开连接
    if (socket) {
        IRG3_LOG_INFO("关闭socket连接:%d", socketFd);
        closesocket(socketFd);
    }
    if (ctx) {
        IRG3_LOG_INFO("释放CTX对象");
        SSL_CTX_free(ctx);
    }
    if (ssl) {
        SSL_shutdown(ssl);
        SSL_free(ssl);
        IRG3_LOG_INFO("关闭SSL连接并释放");
    }
    WSACleanup();
    return stat_;
}



bool irg3_https::tcp_conn(const char* pSvrIp, int iSvrPort, int& socket) {
    socket = ::socket(AF_INET, SOCK_STREAM, 0);
    if (socket == -1) {
        return false;
    }

    sockaddr_in sa;
    sa.sin_addr.s_addr = inet_addr(pSvrIp);
    sa.sin_port = htons(iSvrPort);
    sa.sin_family = AF_INET;

    int retCode = ::connect(socket, (struct sockaddr*)&sa, sizeof(sa));
    int nNetTimeout = 1000;
    setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,(char*) & nNetTimeout, sizeof(int));

    if (retCode == -1) {
        return false;
    }
    return true;
}




bool irg3_https::getGetReqData(const irg3_http_send_head_* head_, std::string& str_) {
    if (head_ == nullptr) {
        return false;
    }
    std::stringstream stream;
    stream << head_->method_ << " "<<head_->pro_<<"://" << head_->host_ << head_->url_ << " " << head_->version_ << "\r\n";
    stream << "Host: " << head_->host_ << "\r\n";
    for (auto& it : head_->map_) {
        stream << it.first << ": " << it.second << "\r\n";
    }
    stream << "\r\n";
    str_ = stream.str();
    return true;
}

bool irg3_https::getPostReqData(const irg3_http_send_head_* head_, std::string& str_) {
    if (head_ == nullptr) {
        return false;
    }

    std::stringstream stream;
    stream << head_->method_ << " " << head_->pro_ << "://" << head_->host_ << head_->url_ << " " << head_->version_ << "\r\n";
    stream << "Host: " << head_->host_ << "\r\n";
    for (auto& it : head_->map_) {
        stream << it.first << ": " << it.second << "\r\n";
    }
    if (!head_->map_.count("Content-Type")) {
        stream << "Content-Type: application/json; charset=utf-8\r\n";
    }
    if (head_->body_[0] == 'i' && head_->body_[1] == '\0') {
        //body里面存放的时文件路径
        str_ = stream.str();
        return true;
    }
    stream << "Content-Length: " << head_->body_size_ << "\r\n\r\n";
    stream << head_->body_ << "\r\n\r\n";
    str_ = stream.str();


    return true;


}



int irg3_https::readResponse(SSL* ssl,char* message_, int message_size_, std::function<bool(const char* msg_)>method_, bool pro_flag_) {
    if (ssl == nullptr) {
        IRG3_LOG_ERROR("ssl为空");
        return -1;
    }
    IRG3_LOG_INFO("开始读取数据");

    bool flag_ = false;
    if (message_ == nullptr || message_size_ == -1) {
        flag_ = true;
        message_ = new char[1025];
        message_size_ = 1024;
        IRG3_LOG_WARN("未指定读取数据的缓存空间,自动创建1024大小的缓存空间");
    }
    int stat_ = -1;

    // 读取响应体数据,一次读1k
    int len_ = 0;
    IRG3_REPORT_TEXT_LOCK;
    if (pro_flag_) {
        IRG3_LOG_INFO("使用http1.1方式读取数据");
        int all_val_ = 0;
        do {
            if (len_ != 0) { 
                message_[len_] = '\0';
                if (stat_ == -1&&method_) {
                    //断言
                    stat_ = method_(message_) ? 0 : 1;
                }
                IRG3_REPORT_TEXT_EX(message_);
            }
            all_val_ -= len_;
            if (all_val_ == 0) {
                //获取下一块数据的大小
                all_val_ = https_1_1(ssl);
            }
            if (all_val_ == 0) { break; }
        } while ((len_ = SSL_read(ssl, message_, min(message_size_, all_val_))) > 0);
    }
    else {
        IRG3_LOG_INFO("使用http1.0方式读取数据");
        while ((len_ = SSL_read(ssl, message_, message_size_)) > 0) {
            message_[len_] = '\0';
            if (stat_ == -1&&method_) {
                //断言
                stat_ = method_(message_)?0:1;
            }
            IRG3_REPORT_TEXT_EX(message_);
            printf("%s", message_);
        }
    }
    IRG3_REPORT_TEXT_EX("\n");
    IRG3_REPORT_TEXT_UNLOCK;
    IRG3_LOG_INFO("结束读取数据");

    if (flag_) {
        delete[]message_;
        message_ = nullptr;
        IRG3_LOG_INFO("释放缓存空间");
    }

    return stat_;
}
int irg3_https::readResponse(int socketFd, char* message_, int message_size_, std::function<bool(const char* msg_)>method_, bool pro_flag_) {
    IRG3_LOG_INFO("开始读取数据");

    bool flag_ = false;
    int stat_ = -1;

    if (message_ == nullptr || message_size_ == -1) {
        flag_ = true;
        message_ = new char[1025];
        message_size_ = 1024;
        IRG3_LOG_WARN("未指定读取数据的缓存空间,自动创建1024大小的缓存空间");
    }


    int len_ = 0;
    IRG3_REPORT_INFO("接收的数据:");
    IRG3_REPORT_TEXT_LOCK;
    if (pro_flag_) {
        IRG3_LOG_INFO("使用http1.1方式读取数据");
        int all_val_ = 0;
        do {
            if (len_ != 0) { 
                message_[len_] = '\0';
                IRG3_REPORT_TEXT_EX(message_);
                if (stat_ == -1 && method_) {
                    //断言
                    stat_ = method_(message_) ? 0 : 1;
                }
            }
            all_val_ -= len_;
            //获取下一块数据的大小
            if (all_val_ == 0) { all_val_ = http_1_1(socketFd); }
            if (all_val_ == 0) { break; }
        } while ((len_ = recv(socketFd, message_, min(message_size_, all_val_), 0)) > 0);
    }
    else {
        IRG3_LOG_INFO("使用http1.0方式读取数据");
        while ((len_ = recv(socketFd, message_, message_size_, 0)) > 0) {
            message_[len_] = '\0';
            IRG3_REPORT_TEXT_EX(message_);
            if (stat_ == -1 && method_) {
                //断言
                stat_ = method_(message_) ? 0 : 1;
            }
            if (len_ != message_size_) {
                break;
            }
        }
    }
    IRG3_REPORT_TEXT_EX("\n");
    IRG3_REPORT_TEXT_UNLOCK;
    IRG3_LOG_INFO("结束读取数据");

    if (flag_) {
        delete[]message_;
        message_ = nullptr;
        IRG3_LOG_INFO("释放缓存空间");
    }

    return stat_;
}

test.cpp(主函数)


#include "irg3_http.h"

int main() {
	irg3_https::getInstance()->https_get("www.baidu.com", "/");
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 语法语义分析是编译器中的一个重要步骤,它主要是对程序代码进行检查和分析,以确保其语法和语义的正确性。中间代码生成是语法语义分析的一个子过程,它将源代码转换为高级语言的中间形式,以便后续的优化和目标代码生成。 在C语言的中间代码生成过程中,主要需要完成以下几个任务: 1. 词法分析:将源代码划分为一个个的词法单元(token),比如关键字、运算符、标识符等。 2. 语法分析:利用语法规则进行句法分析,确认源代码的语法结构是否正确。 3. 语义分析:根据语义规则检查源代码的语义正确性,比如变量类型的匹配、函数调用参数的正确性等。 4. 符号表管理:构建符号表,用于存储变量、函数等的信息,包括名称、类型、作用域等。 5. 类型检查:通过对变量和表达式的类型进行检查,确保其匹配和兼容性。 6. 中间代码生成:将语法分析的结果转换为中间代码表示形式,比如三地址码、四元式、抽象语法树等。 在实现中间代码生成的C语法语义分析过程中,可以借助工具和库来简化开发,比如Flex和Bison。Flex用于生成词法分析器,Bison用于生成语法分析器。可以定义相应的词法规则和语法规则,指定语义规则和动作,以及生成与中间代码相关的数据结构。 通过对源代码进行逐行读取和解析,将其转换为中间代码的表示形式,并将中间代码存储和管理起来,以便后续的编译优化和目标代码生成。 总体来说,实现语法语义分析中间代码生成的C语言实现,需要掌握词法分析、语法分析、语义分析等相关知识,并借助相应的工具和库来简化开发过程。 ### 回答2: 语法语义分析是编译器的重要阶段,该阶段负责检查源代码的语法结构和语义合理性,并将源代码转换为中间代码。中间代码是一种与具体机器无关的表示形式,可以通过不同的后续步骤生成最终的目标代码。 在中间代码生成阶段,我们需要根据源代码的语法结构,将其转换为对应的中间代码。对于C语言来说,可以使用抽象语法树(Abstract Syntax Tree,AST)作为中间表示形式。AST是一种树状结构,用于表示源代码的语法结构和语义。 中间代码生成的过程通常包括以下步骤: 1. 创建抽象语法树(AST):通过解析代码,构建一颗抽象语法树,该树反映了源代码的语法结构。 2. 遍历抽象语法树:通过遍历抽象语法树的节点,根据语法规则和语义规则,生成对应的中间代码。在遍历过程中,我们可以根据不同节点类型进行不同的处理。 3. 生成中间代码:根据遍历过程中的处理逻辑,逐步生成中间代码。这些中间代码可以是三地址码、虚拟机代码或其他形式。 例如,当我们遇到一个赋值表达式时,可以将其转换为对应的中间代码。假设有以下C语言代码: ``` x = a + b * c; ``` 我们可以生成如下的中间代码: ``` t1 = b * c; t2 = a + t1; x = t2; ``` 在生成中间代码的过程中,我们需要处理表达式的优先级、运算符的结合性等问题,并且需要生成临时变量来存储中间结果。这些临时变量在中间代码生成的过程中起到了重要的作用,帮助我们跟踪和管理中间结果。 总之,中间代码生成是编译过程中至关重要的一步,它将源代码转换为机器无关的表示形式,并为后续的优化和目标代码生成提供了基础。 ### 回答3: 语法语义分析是编译过程中的重要阶段,它主要负责对源代码进行分析和处理,以便生成中间代码。在中间代码生成阶段,我们可以将源代码转化为一种中间形式,这种形式更接近于目标机器语言的表示,同时也不涉及到具体的计算机体系结构。 通常,我们会选择一种高级编程语言作为目标语言来生成中间代码,例如C语言。以下是一种实现中间代码生成的简单方法: 1. 通过词法分析和语法分析得到抽象语法树(AST)。 2. 对AST进行语义分析,确定每个语法结构是否符合语言规范,同时收集变量、函数、类型等的信息。 3. 根据语义分析的结果,使用一些规则和模式将AST转换为中间代码。这可以通过递归遍历AST来实现,将每个语法结构转换为等效的中间代码表示。例如,将函数调用转换为中间代码的函数调用指令,将赋值语句转换为中间代码的赋值指令等。 4. 在生成中间代码的过程中,还需要进行符号表的管理,以便实现变量和函数的作用域、访问和传递等操作。 5. 最后,通过一系列优化算法和技术,对生成的中间代码进行优化,以提高其运行效率和可读性。 总之,中间代码的生成是语法语义分析的重要组成部分。通过将源代码转换为中间代码,可以更方便地进行后续优化和目标代码的生成。实现中间代码生成需要深入理解编程语言的语法和语义规则,并灵活运用编译原理的知识和技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值