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

2023-05-16

开发环境: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", "/");
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • Chrome 问题:“无法加载资源:net::ERR_CONNECTION_TIMED_OUT”

    我尝试通过 HTTPS 访问我的 Web 应用程序 它无法加载 JavaScript 文件并显示 无法加载资源 net ERR CONNECTION TIMED OUT 但它在 IE 和 Firefox 中按预期工作 通过 HTTP 在 C
  • 如何查看点击 HTML 按钮时发出的 POST 请求的地址?

    我正在创建一个涉及网络抓取和网络自动化的项目 我想首先提交此表格 http rgsntl rgs cuhk edu hk rws prd applx2 Public tt dsp timetable aspx http rgsntl rgs
  • 如何在java中以编程方式访问网页

    有一个网页 我想从中检索某个字符串 为此 我需要登录 单击一些按钮 填充文本框 单击另一个按钮 然后就会出现字符串 我怎样才能编写一个java程序来自动执行此操作 是否有任何有用的库用于此目的 Thanks Try HtmlUnit htt
  • ASP.NET HTTP 请求是否会转换为 1 个线程?

    可以安全地假设当用户通过 HTTP 请求 aspx 页面时 ASP NET 至少为其创建 1 个线程吗 如果是这样 持续多久 如果 1000 人向同一个 aspx 页面发出 HTTP 请求 是否会涉及一些线程回收 因此不会产生不同的 100
  • 同源政策目的可疑

    正如我所读到的 同源策略是防止源自 邪恶 域 A 的脚本向 良好 域 B 发出请求 换句话说 跨站点请求伪造 玩了一下我了解到的Access Control Allow Origin标头和CORS据我了解 它允许从好域 B 指定服务器 域
  • 如何设置响应文件名而不强制“另存为”对话框

    我在某些响应中返回一个流 设置适当的content type标头 我正在寻找的行为是这样的 如果浏览器能够呈现给定内容类型的内容 那么它应该将其显示在浏览器窗口中 如果浏览器不知道如何呈现内容 那么它应该显示 另存为 对话框 其中文件名应该
  • 使用.pem文件在java中发送https请求

    我有包含证书 私钥和信任链的 pem 文件 以及我使用它生成的 p12 文件openssl pkcs12 导出 openssl pkcs12 export out file p12 in file pem inkey file pem pa
  • HTTP请求的内容长度>正文大小

    我正在管理一个网站 该网站过去几个月在使用 MVC 3 0 ASP net 构建的 IIS 7 5 上运行良好 当我们的 AJAX POST 请求 通过 jQuery 触发 因发布的 JSON 被截断而失败时 我们时不时地会遇到一个问题 到
  • ASP.NET - 将所有 https 请求重写为 http

    我的问题正是所提出的问题here https stackoverflow com questions 16276860 iis 7 adding ssl to one site all other sites responds to htt
  • Java8,HttpClient,接收“收到致命警报:handshake_failure”

    我们正在尝试访问以下 URL 仅作为示例 https broadly vice com en us article eating out as a feminist https broadly vice com en us article
  • 忽略控制台应用程序中的 Web 浏览器 SSL 安全警报

    我正在创建一个能够远程捕获网站屏幕截图的控制台应用程序 除了我无法避免证书错误之外 一切都正常 每次我收到无法通过的弹出消息 我尝试使用 ServicePointManager ServerCertificateValidationCall
  • Flex HTTPservice 和 POST,发送文件?

    我使用基本的 Post 将数据发送到 Django 服务器 数据由flex动态创建的base64编码的640 380 PNG图像组成 成分
  • Spring RestTemplate - 带有请求正文的http GET [重复]

    这个问题在这里已经有答案了 可能的重复 带请求正文的 HTTP GET https stackoverflow com questions 978061 http get with request body 我在这里读过一些不提倡通过 HT
  • 如何找出数据包的HTTP头长度?

    我知道如何手动执行此操作 通过查看十六进制转储 我怎样才能自动获得相同的信息 我必须使用 API 吗 我有wireshark 和Microsoft 网络监视器 这可以通过简单地实现Lua解析器 http wiki wireshark org
  • 使用 CustomBinding 的 WCF 服务配置 HTTPS

    我需要 WCF 服务上的自定义绑定 以允许我将原始内容传递到 WCFRest 服务 效果很好 但我无法让它接受传输级安全性 我想要 https 和 basicauthentication 就像我在其他地方使用的那样 端点看起来像这样
  • 有什么简单的方法可以完全忽略带有 java url 连接的 ssl ?

    我正在构建一个应用程序 定期检查一些 RSS 提要是否有新内容 其中一些提要只能通过 https 访问 有些具有自签名或以某种方式损坏的证书 我仍然希望能够检查它们 请注意 安全性在此应用程序中不是问题 目标是以最小的努力访问内容 我使用此
  • 如何在 G-WAN 中添加 HTTP/2

    我想知道是否可以通过使用解决方案 nghttp2 https nghttp2 org https nghttp2 org 很抱歉这么晚才回答 出于某种原因 Stackoverflow 没有通知我们这个问题 我之所以找到它只是因为收到了更新的
  • Response.Redirect 并不总是重定向

    我们在一个工作不一致的页面上有一个简单的 Response Redirect IIS 6 0 大多数情况下 它会正确重定向 但我们收到一些用户抱怨 他们没有重定向 而是看到 302 对象移至此处 页面 该页面显示标题信息以及正确的位置 如果
  • 如何解析来自基于 C 的 Web 服务器的 HTTP 请求

    我有一个编程项目 我必须创建一个处理 HTTP 请求的多线程 Web 服务器 我刚刚学习了套接字编程 并且运行了一个客户端和一个服务器 我想知道解析 HTTP 请求标头的最佳方法是什么 我看到了这个 如何用C 解析http请求 https
  • 通过 HTTPS 的隧道

    在我的工作场所 流量拦截器 防火墙变得越来越糟糕 我无法通过端口 22 连接到我的家用计算机 并且缺乏 ssh 访问权限让我感到难过 我以前可以通过将 SSH 移动到端口 5050 来使用它 但我认为最近的一些过滤器现在将此流量视为 IM

随机推荐

  • SLAM学习笔记

    编译环境ubuntu20 04 vscode ceres库2 0 0 g2o库同gaoxiang12 slambook2中的版本号一致 cmake文件 cmake minimum required VERSION 2 8 project c
  • 数据结构之C语言单链表操作

    实验目的 xff1a 1 xff0e 创建一个带头结点的单链表 2 xff0e 插入元素操作 xff1a 将新元素x插入到单链表head的头部 将新元素x插入到单链表head的尾部 将新元素x插入到单链表head中第i个元素之后 3 xff
  • DBUS入门与C编程

    https blog csdn net weixin 45566765 article details 125028296 一 D Bus简介 1 D Bus是什么 D Bus最主要的用途是在 Linux 桌面环境为进程提供通信 xff0c
  • 模拟IIC——关于模拟IIC的IO口的配置选取推挽输出还是开漏输出,以及是否需要更改IO口输入输出模式和是否需要对IO配置上拉

    在使用模拟IIC的时候 xff0c 观看别人的程序的时候发现了程序之间的一些不一样的地方 代码1 IO方向设置 define SDA IN GPIOB gt MODER amp 61 3 lt lt 9 2 GPIOB gt MODER 6
  • C语言——链表

    C语言 链表 链表是一种基础的数据结构类型 xff0c 一种能够动态维护数据的线性数据表 链表的数据以结点形式存储信息 xff0c 并通过结点之间的指针实现结点之间的衔接 为什么要用链表 xff1f 链表和数组类似 xff0c 但是功能比数
  • 学习记录 | ZigBee协议栈工作流程

    第一次来CSDN记录一下学习过程 xff0c 其实就是笔记啦 之前用Typora 的 但前几天电脑出问题重装系统后打开笔记文件发现照片都打不开了 xff0c 索性想换一种记笔记方式 好啦 以下是正文 xff01 xff01 xff01 对了
  • 学习记录 | ZigBee协议栈实践——串口收发数据

    上次的理论知识学的有点杂乱 今天来跟着例程实践看一看 目录 一 ZigBee协议栈的安装 编译和下载 二 协议栈工作流程 三 串口通信主要代码 1 串口打印 2 串口打印收到的数据 四 实现 五 总结 一 ZigBee协议栈的安装 编译和下
  • 第一次画异形板后的总结感悟

    目录 画原理图前的准备 画PCB的一些好方法 蜂鸣器电路 供电电路 其他电路 杂七杂八的随记要点 总结 像这样的异形板是通过solidworks扫描实物生成的 画原理图前的准备 当然是选购元器件 一般习惯在嘉立创进行选购 选择符合要求 有库
  • 自学物联网ESP第一天

    先简单的使用串口通信AT指令 刚开始不知道用什么开发好 在博客找了很久本来打算用 eclipse 不过突然发现可以用Arduino 不过下载真的好慢 于是开始看一下函数准备一下 一 基本函数 1 setup 函数 Arduino控制器通电或
  • stm32串口发送+接收

    本文章转自B站江科大自化协 一发送 接线图 目标结构 Serial c include 34 stm32f10x h 34 include lt stdio h gt 1移植printf函数 封装sprintf include lt std
  • vscode配置C/C++环境(超详细保姆级教学)

    大一上学期被学长安利了vscode xff0c 但是下载安装后不会配置 xff0c 自己连查带问搞了七八个小时终于配置好了 后面身边很多同学也不会配 xff0c 都来找我帮忙配 xff0c 加上之前自己摸索着配的时候感觉网上没有详细又靠谱的
  • 路由器接口解析

    ensp 接口解析 路由器的CON和AUX con是串口 xff0c 接口电脑串口可以进路由器的命令行查看和设置 一般为设备的控制端口 xff0c Console端口使用配置专用连线直接连接至计算机的串口 xff0c 利用终端仿真程序进行路
  • 解决VSCode写html文件时<!+Enter导入模板快捷键没办法使用的问题

    好久没有写前端了 xff0c 先是用模板快捷键 lt 43 Enter导入模板 xff0c 发现没有办法使用 找到网上一些资料 xff0c VSCode使用 html 5 取代了之前的生成模板快捷键 示例 xff1a 在空白html文本里逐
  • LVGL 之 windows 上 lvgl 模拟器 基于 Visual Studio 搭建方法的简单整理

    mark GUI Guider与lvgl联合仿真 xff08 结合stm32实验进行演示 xff0c 含触摸屏实验计数器与计算器 xff09 https blog csdn net gudao07 article details 12752
  • 初认识stm32 ————每日小总结(串口通信初始化基础操作)

    留下代码 xff0c 日后复习 c文件 include 34 my uart h 34 void my uart init NVIC PriorityGroupConfig NVIC PriorityGroup 2 设置中断优先级分组 RC
  • Sqoop全量导入mysql表数据到HDFS

    我是在三个节点运行的 xff0c 主节点只有namenode和mysql数据库 1 开启服务 具有NameNode和DataNode start all sh 2 进入sqoop的目录下并且输入代码 下面的命令用于从 MySQL 数据库服务
  • 在字符串中查找子字符串

    查找子串 include lt stdio h gt char Search char str1 char str2 char s1 61 str1 char s2 61 str2 char cur 61 str1 while cur s1
  • 基于相机云台周期运动的“远帧差”图像处理算法

    传统的帧差法针对前后两帧或者某几帧进行图像处理 xff0c 一旦相机视角转动 xff0c 就不能获得很好的检测效果 xff0c 我所参与的项目就是一个云台搭载一台红外相机进行取像 xff0c 这样可以大幅度的节约成本 xff0c 但是对图像
  • 深究C语言4.链表和结构体

    目录 一 xff0c 结构体 一 xff0c 结构体的定义 二 xff0c 结构体的概念 1 2 3 4 5 6 三 xff0c 结构变量的使用 1 结构变量的操作符 gt 和 2 结构变量的赋值 3 结构变量作为函数参数 4 结构指针 5
  • 使用c++解析http\https数据遇到的问题(附代码)

    开发环境 xff1a vs2019 系统 xff1a win10 openssl版本 xff1a Win32OpenSSL 3 0 7 访问网页 xff1a www baidu com 先说明以下问题 xff1a c 43 43 在发送ht