<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlackFeather'S Blog</title><link>http://www.fenlog.com/</link><description>我的技术小博 -- C/C++,Python,Golang</description><generator>RainbowSoft Studio Z-Blog 2.2 Prism Build 140101</generator><language>zh-CN</language><pubDate>Thu, 19 Mar 2026 20:52:56 +0800</pubDate><item><title>sqlite数据库fts5全文索引data数据解析</title><author>345382462@qq.com (blackfeather)</author><link>http://www.fenlog.com/post/135.html</link><pubDate>Wed, 25 Jun 2025 15:06:08 +0800</pubDate><guid>http://www.fenlog.com/post/135.html</guid><description><![CDATA[<p><br/></p><p><br/></p><p>sqlite引入的fts5扩展功能，会创建table_content/table_data等数据表，其中_data中是索引数据，可以尝试解析还原出很多有趣的内容。</p><p><br/></p><p>至于数据结构后面有心情了再补充，直接贴代码。</p><p><br/></p><p>此代码为C++类。</p><p><br/></p><p><br/></p><pre class="brush:cpp;toolbar:false">#pragma&nbsp;once

#include&nbsp;&lt;string&gt;
#include&nbsp;&lt;vector&gt;
#include&nbsp;&lt;map&gt;

#define&nbsp;_fts5GetVarint32(a,b)&nbsp;Fts5GetVarint32((const&nbsp;unsigned&nbsp;char*)a,(uint32_t*)&amp;b)
#define&nbsp;_fts5GetVarint(a,b)&nbsp;&nbsp;&nbsp;&nbsp;Fts5GetVarint((const&nbsp;unsigned&nbsp;char*)a,b)

#define&nbsp;_fts5FastGetVarint32(a,&nbsp;iOff,&nbsp;nVal)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
&nbsp;&nbsp;nVal&nbsp;=&nbsp;(a)[iOff++];&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
&nbsp;&nbsp;if(&nbsp;nVal&nbsp;&amp;&nbsp;0x80&nbsp;){&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
&nbsp;&nbsp;&nbsp;&nbsp;iOff--;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
&nbsp;&nbsp;&nbsp;&nbsp;iOff&nbsp;+=&nbsp;_fts5GetVarint32(&amp;(a)[iOff],&nbsp;nVal);&nbsp;&nbsp;&nbsp;&nbsp;\
&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
}


class&nbsp;FTS5DataHandler
{
public:
	FTS5DataHandler()&nbsp;=&nbsp;default;

#define&nbsp;FTS5_DATA_ID_B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;16&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Max&nbsp;seg&nbsp;id&nbsp;number&nbsp;65535&nbsp;*/
#define&nbsp;FTS5_DATA_DLI_B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Doclist-index&nbsp;flag&nbsp;(1&nbsp;bit)&nbsp;*/
#define&nbsp;FTS5_DATA_HEIGHT_B&nbsp;&nbsp;5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Max&nbsp;dlidx&nbsp;tree&nbsp;height&nbsp;of&nbsp;32&nbsp;*/
#define&nbsp;FTS5_DATA_PAGE_B&nbsp;&nbsp;&nbsp;31&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Max&nbsp;page&nbsp;number&nbsp;of&nbsp;2147483648&nbsp;*/

	static&nbsp;void&nbsp;fts5DecodeRowid(
		int64_t&nbsp;iRowid,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Rowid&nbsp;from&nbsp;%_data&nbsp;table&nbsp;*/
		int*&nbsp;piSegid,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;OUT:&nbsp;Segment&nbsp;id&nbsp;*/
		int*&nbsp;pbDlidx,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;OUT:&nbsp;Dlidx&nbsp;flag&nbsp;*/
		int*&nbsp;piHeight,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;OUT:&nbsp;Height&nbsp;*/
		int*&nbsp;piPgno&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;OUT:&nbsp;Page&nbsp;number&nbsp;*/
	)&nbsp;{
		*piPgno&nbsp;=&nbsp;(int)(iRowid&nbsp;&amp;&nbsp;(((int64_t)1&nbsp;&lt;&lt;&nbsp;FTS5_DATA_PAGE_B)&nbsp;-&nbsp;1));
		iRowid&nbsp;&gt;&gt;=&nbsp;FTS5_DATA_PAGE_B;

		*piHeight&nbsp;=&nbsp;(int)(iRowid&nbsp;&amp;&nbsp;(((int64_t)1&nbsp;&lt;&lt;&nbsp;FTS5_DATA_HEIGHT_B)&nbsp;-&nbsp;1));
		iRowid&nbsp;&gt;&gt;=&nbsp;FTS5_DATA_HEIGHT_B;

		*pbDlidx&nbsp;=&nbsp;(int)(iRowid&nbsp;&amp;&nbsp;0x0001);
		iRowid&nbsp;&gt;&gt;=&nbsp;FTS5_DATA_DLI_B;

		*piSegid&nbsp;=&nbsp;(int)(iRowid&nbsp;&amp;&nbsp;(((int64_t)1&nbsp;&lt;&lt;&nbsp;FTS5_DATA_ID_B)&nbsp;-&nbsp;1));
	}

	static&nbsp;std::pair&lt;std::string,&nbsp;bool&gt;&nbsp;ParserFts5BlockOverData(const&nbsp;std::string&amp;&nbsp;block)
	{
		int&nbsp;nDataLen&nbsp;=&nbsp;(int)block.length();
		if&nbsp;(nDataLen&nbsp;&lt;&nbsp;4)
			return&nbsp;{};

		const&nbsp;char*&nbsp;pData&nbsp;=&nbsp;block.c_str();

		int&nbsp;iRowidOff&nbsp;=&nbsp;fts5GetU16((const&nbsp;unsigned&nbsp;char*)&amp;pData[0]);
		int&nbsp;iPgidx&nbsp;=&nbsp;fts5GetU16((const&nbsp;unsigned&nbsp;char*)&amp;pData[2]);&nbsp;//词条数据总长度
		if&nbsp;(iPgidx&nbsp;&gt;&nbsp;nDataLen)
			return&nbsp;{};
		if&nbsp;(iPgidx&nbsp;==&nbsp;nDataLen)
			return&nbsp;{&nbsp;block.substr(4,&nbsp;iPgidx&nbsp;-&nbsp;4),&nbsp;true&nbsp;};

		int&nbsp;iOverdataLen&nbsp;=&nbsp;0;
		_fts5GetVarint32(&amp;pData[iPgidx],&nbsp;iOverdataLen);

		if&nbsp;(iOverdataLen&nbsp;&lt;=&nbsp;4)
			return&nbsp;{};
		if&nbsp;(iOverdataLen&nbsp;&gt;=&nbsp;nDataLen)
			return&nbsp;{};

		return&nbsp;{&nbsp;block.substr(4,&nbsp;iOverdataLen&nbsp;-&nbsp;4),&nbsp;false&nbsp;};
	}

	void&nbsp;ParserFts5BlockData(const&nbsp;std::string&amp;&nbsp;block,&nbsp;const&nbsp;std::vector&lt;std::string&gt;&amp;&nbsp;overdatas)
	{
		if&nbsp;(block.length()&nbsp;&lt;&nbsp;4)
			return;

		const&nbsp;char*&nbsp;pData&nbsp;=&nbsp;block.data();
		int&nbsp;nDataSize&nbsp;=&nbsp;(int)block.length();

		int&nbsp;iRowidOff&nbsp;=&nbsp;fts5GetU16((const&nbsp;unsigned&nbsp;char*)&amp;pData[0]);&nbsp;//上一条数据的pos&nbsp;list的over&nbsp;data&nbsp;len&nbsp;+&nbsp;4
		int&nbsp;iPgidx&nbsp;=&nbsp;fts5GetU16((const&nbsp;unsigned&nbsp;char*)&amp;pData[2]);&nbsp;//词条数据总长度
		if&nbsp;(iPgidx&nbsp;&gt;=&nbsp;nDataSize)
			return;

		int&nbsp;iTermOff&nbsp;=&nbsp;0;
		int&nbsp;iPgidxOff&nbsp;=&nbsp;iPgidx;
		iPgidxOff&nbsp;+=&nbsp;_fts5GetVarint32(&amp;pData[iPgidxOff],&nbsp;iTermOff);&nbsp;//上一条数据的doclist的over&nbsp;data

		assert(iTermOff);
		std::string&nbsp;last_block;
		int&nbsp;iLastTermLen&nbsp;=&nbsp;0;
		std::vector&lt;int&gt;&nbsp;overdataLens;

		//解析词条数据
		bool&nbsp;bFirst&nbsp;=&nbsp;true;
		std::string&nbsp;word;
		while&nbsp;(true)
		{
			int&nbsp;nTermLen;
			if&nbsp;(iPgidxOff&nbsp;&lt;&nbsp;nDataSize)
			{
				iPgidxOff&nbsp;+=&nbsp;_fts5GetVarint32(&amp;pData[iPgidxOff],&nbsp;nTermLen);
			}
			else&nbsp;if&nbsp;(iTermOff&nbsp;&lt;&nbsp;iPgidx)
			{
				//末尾块
				nTermLen&nbsp;=&nbsp;iPgidx&nbsp;-&nbsp;iTermOff;
				if&nbsp;(!overdatas.empty())
				{
					std::string&nbsp;overdata;
					for&nbsp;(const&nbsp;auto&amp;&nbsp;data&nbsp;:&nbsp;overdatas)
					{
						overdata.append(data);
						overdataLens.emplace_back((int)data.length());
					}

					last_block&nbsp;=&nbsp;block.substr(iTermOff,&nbsp;nTermLen);
					last_block.append(overdata);

					//修正变量
					iLastTermLen&nbsp;=&nbsp;nTermLen;&nbsp;//用于重置overdata的rowid索引
					nTermLen&nbsp;+=&nbsp;(int)overdata.length();
					iPgidx&nbsp;=&nbsp;nTermLen;
					iTermOff&nbsp;=&nbsp;0;
					pData&nbsp;=&nbsp;last_block.data();
				}
			}
			else
			{
				//数据解析到头了
				break;
			}

			if&nbsp;(iTermOff&nbsp;+&nbsp;nTermLen&nbsp;&gt;&nbsp;iPgidx)
				break;
			int&nbsp;iTermEnd&nbsp;=&nbsp;iTermOff&nbsp;+&nbsp;nTermLen;
			//fmtlog(&quot;term&nbsp;len:{},&nbsp;offset:0x{:x}&quot;,&nbsp;nTermLen,&nbsp;iTermOff);

			//读取词条keep
			int&nbsp;iWordKeep&nbsp;=&nbsp;0;
			if&nbsp;(!bFirst)
			{
				_fts5FastGetVarint32(pData,&nbsp;iTermOff,&nbsp;iWordKeep);
				if&nbsp;(iWordKeep&nbsp;&lt;=&nbsp;0)
					break;
				iWordKeep--;
			}

			//读取词条长度
			int&nbsp;nWordLen;
			_fts5FastGetVarint32(pData,&nbsp;iTermOff,&nbsp;nWordLen);
			if&nbsp;(nWordLen&nbsp;&lt;=&nbsp;0)
				break;
			if&nbsp;(iTermOff&nbsp;+&nbsp;nWordLen&nbsp;&gt;=&nbsp;iPgidx)
				break;

			//读取词条
			if&nbsp;(bFirst&nbsp;&amp;&amp;&nbsp;nWordLen&nbsp;&gt;&nbsp;1)
			{
				//跳过一个字节
				if&nbsp;(nWordLen&nbsp;&gt;&nbsp;1)
					word.assign(&amp;pData[iTermOff&nbsp;+&nbsp;1],&nbsp;nWordLen&nbsp;-&nbsp;1);
				else
					word.assign(&amp;pData[iTermOff],&nbsp;nWordLen);&nbsp;//理论上不应该到这里

				bFirst&nbsp;=&nbsp;false;
			}
			else
			{
				if&nbsp;(iWordKeep)
				{
					if&nbsp;(iWordKeep&nbsp;&gt;&nbsp;(int)word.length())
						break;

					word.assign(word,&nbsp;0,&nbsp;iWordKeep);
					word.append(&amp;pData[iTermOff],&nbsp;nWordLen);
				}
				else
				{
					word.assign(&amp;pData[iTermOff],&nbsp;nWordLen);
				}
			}
			iTermOff&nbsp;+=&nbsp;nWordLen;
			//fmtlog(&quot;word:{}({}),&nbsp;keep:{}&quot;,&nbsp;Utf82GBK(word),&nbsp;String2Hex(word),&nbsp;iWordKeep);
			//&nbsp;		if&nbsp;(word&nbsp;==&nbsp;&quot;j&quot;)
			//&nbsp;			int&nbsp;b&nbsp;=&nbsp;0;

			//解析docList
			int&nbsp;nRowID&nbsp;=&nbsp;0;
			while&nbsp;(iTermOff&nbsp;&lt;&nbsp;iTermEnd)
			{
				if&nbsp;(iLastTermLen&nbsp;&amp;&amp;&nbsp;iTermOff&nbsp;&gt;=&nbsp;iLastTermLen)
				{
					//重置rowid
					nRowID&nbsp;=&nbsp;0;

					if&nbsp;(!overdataLens.empty())
					{
						iLastTermLen&nbsp;+=&nbsp;overdataLens[0];
						overdataLens.erase(overdataLens.begin());
					}
					else
					{
						iLastTermLen&nbsp;=&nbsp;0;
					}
				}

				//读取rowid(docid)
				int&nbsp;nRowidTmp&nbsp;=&nbsp;0;
				iTermOff&nbsp;+=&nbsp;_fts5GetVarint32(&amp;pData[iTermOff],&nbsp;nRowidTmp);
				//增量
				nRowID&nbsp;+=&nbsp;nRowidTmp;
				//fmtlog(&quot;	rowid:{}&quot;,&nbsp;nRowID);

				//读取pos&nbsp;list&nbsp;len
				int&nbsp;nPosListSize&nbsp;=&nbsp;0;
				iTermOff&nbsp;+=&nbsp;_fts5GetVarint32(&amp;pData[iTermOff],&nbsp;nPosListSize);
				if&nbsp;(nPosListSize&nbsp;&lt;&nbsp;0)
					break;
				nPosListSize&nbsp;/=&nbsp;2;
				//fmtlog(&quot;	pos&nbsp;list&nbsp;size:{}&quot;,&nbsp;nPosListSize);
				if&nbsp;(iTermOff&nbsp;+&nbsp;nPosListSize&nbsp;&gt;&nbsp;iTermEnd)
					break;

				if&nbsp;(nPosListSize&nbsp;==&nbsp;0)
					continue;

				//读取pos&nbsp;list
				auto&nbsp;poslist&nbsp;=&nbsp;fts5DecodePosList(&amp;pData[iTermOff],&nbsp;nPosListSize);
				if&nbsp;(m_rowidsFilter.empty()&nbsp;||&nbsp;!m_rowidsFilter.HasKey(nRowID))
				{
					//printf(&quot;word:%s(%s),&nbsp;keep:%d&nbsp;\n&quot;,&nbsp;Utf82GBK(word).c_str(),&nbsp;String2Hex(word).c_str(),&nbsp;iWordKeep);
					//printf(&quot;	rowid:%d&nbsp;\n&quot;,&nbsp;nRowID);
					//printf(&quot;	pos&nbsp;list&nbsp;size:%d&nbsp;\n&quot;,&nbsp;nPosListSize);
					//fmtlog(&quot;	pos&nbsp;list:{}&quot;,&nbsp;poslist);

					for&nbsp;(const&nbsp;auto&amp;&nbsp;posinfo&nbsp;:&nbsp;poslist)
					{
						for&nbsp;(const&nbsp;auto&amp;&nbsp;pos&nbsp;:&nbsp;posinfo.second)
						{
							m_result[nRowID][posinfo.first].emplace(pos,&nbsp;word);
							//auto&nbsp;ret&nbsp;=&nbsp;m_result[nRowID][posinfo.first].emplace(pos,&nbsp;word);
//&nbsp;							if&nbsp;(!ret.second)
//&nbsp;								printf(&quot;break;\n&quot;);
						}
					}
				}
				iTermOff&nbsp;+=&nbsp;nPosListSize;
			}
		}
	}

	inline&nbsp;void&nbsp;SetRowidFilter(int&nbsp;nRowid)
	{
		m_rowidsFilter.emplace(nRowid);
	}

	inline&nbsp;void&nbsp;ClearRowidFilter()
	{
		m_rowidsFilter.clear();
	}

	std::map&lt;int,&nbsp;std::map&lt;int,&nbsp;std::string&gt;&gt;&nbsp;GetResult()
	{
		//整理词语转为真正的句子
		//std::map&lt;int,&nbsp;std::map&lt;int,&nbsp;std::vector&lt;std::pair&lt;std::string,&nbsp;std::vector&lt;int&gt;&gt;&gt;&gt;&gt;&nbsp;m_result;
		//		rowid		&nbsp;&nbsp;colid&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;content
		std::map&lt;int,&nbsp;std::map&lt;int,&nbsp;std::string&gt;&gt;&nbsp;ret;
		for&nbsp;(auto&amp;&nbsp;kv&nbsp;:&nbsp;m_result)
		{
			int&nbsp;nRowid&nbsp;=&nbsp;kv.first;
			for&nbsp;(auto&amp;&nbsp;rowinfo&nbsp;:&nbsp;kv.second)
			{
				int&nbsp;nColid&nbsp;=&nbsp;rowinfo.first;

				//合并字符串
				std::string&nbsp;content;
				std::for_each(rowinfo.second.begin(),&nbsp;rowinfo.second.end(),&nbsp;[&amp;content](const&nbsp;std::pair&lt;int,&nbsp;std::string&gt;&nbsp;&amp;p)&nbsp;{
					content.append(p.second);
				});

				ret[nRowid][nColid]&nbsp;=&nbsp;content;
				//ret[nRowid][nColid]&nbsp;=&nbsp;Utf82GBK(content);
			}
		}

#ifdef&nbsp;_DEBUG
		//fmtlog(&quot;ret:{}&quot;,&nbsp;ret);
#endif&nbsp;//&nbsp;_DEBUG

		return&nbsp;ret;
	}

private:

	FKeySet&lt;int&gt;&nbsp;m_rowidsFilter;

	//结果集
	//		rowid		&nbsp;&nbsp;colid			index&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;word
	std::map&lt;int,&nbsp;std::map&lt;int,&nbsp;std::map&lt;int,&nbsp;std::string,&nbsp;std::less&lt;&gt;&gt;&gt;&gt;&nbsp;m_result;

	//列id-pos&nbsp;list
	//				col&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pos&nbsp;list
	static&nbsp;std::map&lt;int,&nbsp;std::vector&lt;int&gt;&gt;&nbsp;fts5DecodePosList(const&nbsp;char*&nbsp;pData,&nbsp;int&nbsp;nPosListSize)
	{
		std::map&lt;int,&nbsp;std::vector&lt;int&gt;&gt;&nbsp;ret;
		int&nbsp;nPos&nbsp;=&nbsp;0;
		int&nbsp;nDocOff&nbsp;=&nbsp;0;
		int&nbsp;nColID&nbsp;=&nbsp;0;
		int&nbsp;nPosID&nbsp;=&nbsp;0;
		while&nbsp;(nDocOff&nbsp;&lt;&nbsp;nPosListSize)
		{
			int&nbsp;nPos;
			_fts5FastGetVarint32(pData,&nbsp;nDocOff,&nbsp;nPos);
			if&nbsp;(nPos&nbsp;&lt;=&nbsp;0)
				break;

			if&nbsp;(nPos&nbsp;==&nbsp;1)
			{
				//列切换
				int&nbsp;nColOff;
				_fts5FastGetVarint32(pData,&nbsp;nDocOff,&nbsp;nColOff);
				if&nbsp;(nColOff&nbsp;&lt;=&nbsp;0)
					break;
				nColID&nbsp;+=&nbsp;nColOff;
				nPosID&nbsp;=&nbsp;0;
				continue;
			}

			nPosID&nbsp;+=&nbsp;nPos&nbsp;-&nbsp;2;
			ret[nColID].emplace_back(nPosID);
		}

		return&nbsp;ret;
	}


#define&nbsp;SLOT_2_0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x001fc07f
#define&nbsp;SLOT_4_2_0&nbsp;&nbsp;&nbsp;0xf01fc07f

	static&nbsp;uint16_t&nbsp;fts5GetU16(const&nbsp;unsigned&nbsp;char*&nbsp;aIn)&nbsp;
	{
		return&nbsp;((uint16_t)aIn[0]&nbsp;&lt;&lt;&nbsp;8)&nbsp;+&nbsp;aIn[1];
	}


	static&nbsp;uint8_t&nbsp;Fts5GetVarint(const&nbsp;unsigned&nbsp;char*&nbsp;p,&nbsp;uint64_t*&nbsp;v)&nbsp;{
		uint32_t&nbsp;a,&nbsp;b,&nbsp;s;

		a&nbsp;=&nbsp;*p;
		/*&nbsp;a:&nbsp;p0&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(a&nbsp;&amp;&nbsp;0x80))
		{
			*v&nbsp;=&nbsp;a;
			return&nbsp;1;
		}

		p++;
		b&nbsp;=&nbsp;*p;
		/*&nbsp;b:&nbsp;p1&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(b&nbsp;&amp;&nbsp;0x80))
		{
			a&nbsp;&amp;=&nbsp;0x7f;
			a&nbsp;=&nbsp;a&nbsp;&lt;&lt;&nbsp;7;
			a&nbsp;|=&nbsp;b;
			*v&nbsp;=&nbsp;a;
			return&nbsp;2;
		}

		/*&nbsp;Verify&nbsp;that&nbsp;constants&nbsp;are&nbsp;precomputed&nbsp;correctly&nbsp;*/
		assert(SLOT_2_0&nbsp;==&nbsp;((0x7f&nbsp;&lt;&lt;&nbsp;14)&nbsp;|&nbsp;(0x7f)));
		assert(SLOT_4_2_0&nbsp;==&nbsp;((0xfU&nbsp;&lt;&lt;&nbsp;28)&nbsp;|&nbsp;(0x7f&nbsp;&lt;&lt;&nbsp;14)&nbsp;|&nbsp;(0x7f)));

		p++;
		a&nbsp;=&nbsp;a&nbsp;&lt;&lt;&nbsp;14;
		a&nbsp;|=&nbsp;*p;
		/*&nbsp;a:&nbsp;p0&lt;&lt;14&nbsp;|&nbsp;p2&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(a&nbsp;&amp;&nbsp;0x80))
		{
			a&nbsp;&amp;=&nbsp;SLOT_2_0;
			b&nbsp;&amp;=&nbsp;0x7f;
			b&nbsp;=&nbsp;b&nbsp;&lt;&lt;&nbsp;7;
			a&nbsp;|=&nbsp;b;
			*v&nbsp;=&nbsp;a;
			return&nbsp;3;
		}

		/*&nbsp;CSE1&nbsp;from&nbsp;below&nbsp;*/
		a&nbsp;&amp;=&nbsp;SLOT_2_0;
		p++;
		b&nbsp;=&nbsp;b&nbsp;&lt;&lt;&nbsp;14;
		b&nbsp;|=&nbsp;*p;
		/*&nbsp;b:&nbsp;p1&lt;&lt;14&nbsp;|&nbsp;p3&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(b&nbsp;&amp;&nbsp;0x80))
		{
			b&nbsp;&amp;=&nbsp;SLOT_2_0;
			/*&nbsp;moved&nbsp;CSE1&nbsp;up&nbsp;*/
			/*&nbsp;a&nbsp;&amp;=&nbsp;(0x7f&lt;&lt;14)|(0x7f);&nbsp;*/
			a&nbsp;=&nbsp;a&nbsp;&lt;&lt;&nbsp;7;
			a&nbsp;|=&nbsp;b;
			*v&nbsp;=&nbsp;a;
			return&nbsp;4;
		}

		/*&nbsp;a:&nbsp;p0&lt;&lt;14&nbsp;|&nbsp;p2&nbsp;(masked)&nbsp;*/
		/*&nbsp;b:&nbsp;p1&lt;&lt;14&nbsp;|&nbsp;p3&nbsp;(unmasked)&nbsp;*/
		/*&nbsp;1:save&nbsp;off&nbsp;p0&lt;&lt;21&nbsp;|&nbsp;p1&lt;&lt;14&nbsp;|&nbsp;p2&lt;&lt;7&nbsp;|&nbsp;p3&nbsp;(masked)&nbsp;*/
		/*&nbsp;moved&nbsp;CSE1&nbsp;up&nbsp;*/
		/*&nbsp;a&nbsp;&amp;=&nbsp;(0x7f&lt;&lt;14)|(0x7f);&nbsp;*/
		b&nbsp;&amp;=&nbsp;SLOT_2_0;
		s&nbsp;=&nbsp;a;
		/*&nbsp;s:&nbsp;p0&lt;&lt;14&nbsp;|&nbsp;p2&nbsp;(masked)&nbsp;*/

		p++;
		a&nbsp;=&nbsp;a&nbsp;&lt;&lt;&nbsp;14;
		a&nbsp;|=&nbsp;*p;
		/*&nbsp;a:&nbsp;p0&lt;&lt;28&nbsp;|&nbsp;p2&lt;&lt;14&nbsp;|&nbsp;p4&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(a&nbsp;&amp;&nbsp;0x80))
		{
			/*&nbsp;we&nbsp;can&nbsp;skip&nbsp;these&nbsp;cause&nbsp;they&nbsp;were&nbsp;(effectively)&nbsp;done&nbsp;above&nbsp;in&nbsp;calc&#39;ing&nbsp;s&nbsp;*/
			/*&nbsp;a&nbsp;&amp;=&nbsp;(0x7f&lt;&lt;28)|(0x7f&lt;&lt;14)|(0x7f);&nbsp;*/
			/*&nbsp;b&nbsp;&amp;=&nbsp;(0x7f&lt;&lt;14)|(0x7f);&nbsp;*/
			b&nbsp;=&nbsp;b&nbsp;&lt;&lt;&nbsp;7;
			a&nbsp;|=&nbsp;b;
			s&nbsp;=&nbsp;s&nbsp;&gt;&gt;&nbsp;18;
			*v&nbsp;=&nbsp;((uint64_t)s)&nbsp;&lt;&lt;&nbsp;32&nbsp;|&nbsp;a;
			return&nbsp;5;
		}

		/*&nbsp;2:save&nbsp;off&nbsp;p0&lt;&lt;21&nbsp;|&nbsp;p1&lt;&lt;14&nbsp;|&nbsp;p2&lt;&lt;7&nbsp;|&nbsp;p3&nbsp;(masked)&nbsp;*/
		s&nbsp;=&nbsp;s&nbsp;&lt;&lt;&nbsp;7;
		s&nbsp;|=&nbsp;b;
		/*&nbsp;s:&nbsp;p0&lt;&lt;21&nbsp;|&nbsp;p1&lt;&lt;14&nbsp;|&nbsp;p2&lt;&lt;7&nbsp;|&nbsp;p3&nbsp;(masked)&nbsp;*/

		p++;
		b&nbsp;=&nbsp;b&nbsp;&lt;&lt;&nbsp;14;
		b&nbsp;|=&nbsp;*p;
		/*&nbsp;b:&nbsp;p1&lt;&lt;28&nbsp;|&nbsp;p3&lt;&lt;14&nbsp;|&nbsp;p5&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(b&nbsp;&amp;&nbsp;0x80))
		{
			/*&nbsp;we&nbsp;can&nbsp;skip&nbsp;this&nbsp;cause&nbsp;it&nbsp;was&nbsp;(effectively)&nbsp;done&nbsp;above&nbsp;in&nbsp;calc&#39;ing&nbsp;s&nbsp;*/
			/*&nbsp;b&nbsp;&amp;=&nbsp;(0x7f&lt;&lt;28)|(0x7f&lt;&lt;14)|(0x7f);&nbsp;*/
			a&nbsp;&amp;=&nbsp;SLOT_2_0;
			a&nbsp;=&nbsp;a&nbsp;&lt;&lt;&nbsp;7;
			a&nbsp;|=&nbsp;b;
			s&nbsp;=&nbsp;s&nbsp;&gt;&gt;&nbsp;18;
			*v&nbsp;=&nbsp;((uint64_t)s)&nbsp;&lt;&lt;&nbsp;32&nbsp;|&nbsp;a;
			return&nbsp;6;
		}

		p++;
		a&nbsp;=&nbsp;a&nbsp;&lt;&lt;&nbsp;14;
		a&nbsp;|=&nbsp;*p;
		/*&nbsp;a:&nbsp;p2&lt;&lt;28&nbsp;|&nbsp;p4&lt;&lt;14&nbsp;|&nbsp;p6&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(a&nbsp;&amp;&nbsp;0x80))
		{
			a&nbsp;&amp;=&nbsp;SLOT_4_2_0;
			b&nbsp;&amp;=&nbsp;SLOT_2_0;
			b&nbsp;=&nbsp;b&nbsp;&lt;&lt;&nbsp;7;
			a&nbsp;|=&nbsp;b;
			s&nbsp;=&nbsp;s&nbsp;&gt;&gt;&nbsp;11;
			*v&nbsp;=&nbsp;((uint64_t)s)&nbsp;&lt;&lt;&nbsp;32&nbsp;|&nbsp;a;
			return&nbsp;7;
		}

		/*&nbsp;CSE2&nbsp;from&nbsp;below&nbsp;*/
		a&nbsp;&amp;=&nbsp;SLOT_2_0;
		p++;
		b&nbsp;=&nbsp;b&nbsp;&lt;&lt;&nbsp;14;
		b&nbsp;|=&nbsp;*p;
		/*&nbsp;b:&nbsp;p3&lt;&lt;28&nbsp;|&nbsp;p5&lt;&lt;14&nbsp;|&nbsp;p7&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(b&nbsp;&amp;&nbsp;0x80))
		{
			b&nbsp;&amp;=&nbsp;SLOT_4_2_0;
			/*&nbsp;moved&nbsp;CSE2&nbsp;up&nbsp;*/
			/*&nbsp;a&nbsp;&amp;=&nbsp;(0x7f&lt;&lt;14)|(0x7f);&nbsp;*/
			a&nbsp;=&nbsp;a&nbsp;&lt;&lt;&nbsp;7;
			a&nbsp;|=&nbsp;b;
			s&nbsp;=&nbsp;s&nbsp;&gt;&gt;&nbsp;4;
			*v&nbsp;=&nbsp;((uint64_t)s)&nbsp;&lt;&lt;&nbsp;32&nbsp;|&nbsp;a;
			return&nbsp;8;
		}

		p++;
		a&nbsp;=&nbsp;a&nbsp;&lt;&lt;&nbsp;15;
		a&nbsp;|=&nbsp;*p;
		/*&nbsp;a:&nbsp;p4&lt;&lt;29&nbsp;|&nbsp;p6&lt;&lt;15&nbsp;|&nbsp;p8&nbsp;(unmasked)&nbsp;*/

		/*&nbsp;moved&nbsp;CSE2&nbsp;up&nbsp;*/
		/*&nbsp;a&nbsp;&amp;=&nbsp;(0x7f&lt;&lt;29)|(0x7f&lt;&lt;15)|(0xff);&nbsp;*/
		b&nbsp;&amp;=&nbsp;SLOT_2_0;
		b&nbsp;=&nbsp;b&nbsp;&lt;&lt;&nbsp;8;
		a&nbsp;|=&nbsp;b;

		s&nbsp;=&nbsp;s&nbsp;&lt;&lt;&nbsp;4;
		b&nbsp;=&nbsp;p[-4];
		b&nbsp;&amp;=&nbsp;0x7f;
		b&nbsp;=&nbsp;b&nbsp;&gt;&gt;&nbsp;3;
		s&nbsp;|=&nbsp;b;

		*v&nbsp;=&nbsp;((uint64_t)s)&nbsp;&lt;&lt;&nbsp;32&nbsp;|&nbsp;a;

		return&nbsp;9;
	}

	static&nbsp;int&nbsp;Fts5GetVarint32(const&nbsp;unsigned&nbsp;char*&nbsp;p,&nbsp;uint32_t*&nbsp;v)&nbsp;{
		uint32_t&nbsp;a,&nbsp;b;

		/*&nbsp;The&nbsp;1-byte&nbsp;case.&nbsp;Overwhelmingly&nbsp;the&nbsp;most&nbsp;common.&nbsp;*/
		a&nbsp;=&nbsp;*p;
		/*&nbsp;a:&nbsp;p0&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(a&nbsp;&amp;&nbsp;0x80))
		{
			/*&nbsp;Values&nbsp;between&nbsp;0&nbsp;and&nbsp;127&nbsp;*/
			*v&nbsp;=&nbsp;a;
			return&nbsp;1;
		}

		/*&nbsp;The&nbsp;2-byte&nbsp;case&nbsp;*/
		p++;
		b&nbsp;=&nbsp;*p;
		/*&nbsp;b:&nbsp;p1&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(b&nbsp;&amp;&nbsp;0x80))
		{
			/*&nbsp;Values&nbsp;between&nbsp;128&nbsp;and&nbsp;16383&nbsp;*/
			a&nbsp;&amp;=&nbsp;0x7f;
			a&nbsp;=&nbsp;a&nbsp;&lt;&lt;&nbsp;7;
			*v&nbsp;=&nbsp;a&nbsp;|&nbsp;b;
			return&nbsp;2;
		}

		/*&nbsp;The&nbsp;3-byte&nbsp;case&nbsp;*/
		p++;
		a&nbsp;=&nbsp;a&nbsp;&lt;&lt;&nbsp;14;
		a&nbsp;|=&nbsp;*p;
		/*&nbsp;a:&nbsp;p0&lt;&lt;14&nbsp;|&nbsp;p2&nbsp;(unmasked)&nbsp;*/
		if&nbsp;(!(a&nbsp;&amp;&nbsp;0x80))
		{
			/*&nbsp;Values&nbsp;between&nbsp;16384&nbsp;and&nbsp;2097151&nbsp;*/
			a&nbsp;&amp;=&nbsp;(0x7f&nbsp;&lt;&lt;&nbsp;14)&nbsp;|&nbsp;(0x7f);
			b&nbsp;&amp;=&nbsp;0x7f;
			b&nbsp;=&nbsp;b&nbsp;&lt;&lt;&nbsp;7;
			*v&nbsp;=&nbsp;a&nbsp;|&nbsp;b;
			return&nbsp;3;
		}

		/*&nbsp;A&nbsp;32-bit&nbsp;varint&nbsp;is&nbsp;used&nbsp;to&nbsp;store&nbsp;size&nbsp;information&nbsp;in&nbsp;btrees.
		**&nbsp;Objects&nbsp;are&nbsp;rarely&nbsp;larger&nbsp;than&nbsp;2MiB&nbsp;limit&nbsp;of&nbsp;a&nbsp;3-byte&nbsp;varint.
		**&nbsp;A&nbsp;3-byte&nbsp;varint&nbsp;is&nbsp;sufficient,&nbsp;for&nbsp;example,&nbsp;to&nbsp;record&nbsp;the&nbsp;size
		**&nbsp;of&nbsp;a&nbsp;1048569-byte&nbsp;BLOB&nbsp;or&nbsp;string.
		**
		**&nbsp;We&nbsp;only&nbsp;unroll&nbsp;the&nbsp;first&nbsp;1-,&nbsp;2-,&nbsp;and&nbsp;3-&nbsp;byte&nbsp;cases.&nbsp;&nbsp;The&nbsp;very
		**&nbsp;rare&nbsp;larger&nbsp;cases&nbsp;can&nbsp;be&nbsp;handled&nbsp;by&nbsp;the&nbsp;slower&nbsp;64-bit&nbsp;varint
		**&nbsp;routine.
		*/
		{
			uint64_t&nbsp;v64;
			uint8_t&nbsp;n;
			p&nbsp;-=&nbsp;2;
			n&nbsp;=&nbsp;Fts5GetVarint(p,&nbsp;&amp;v64);
			*v&nbsp;=&nbsp;((uint32_t)v64)&nbsp;&amp;&nbsp;0x7FFFFFFF;
			assert(n&nbsp;&gt;&nbsp;3&nbsp;&amp;&amp;&nbsp;n&nbsp;&lt;=&nbsp;9);
			return&nbsp;n;
		}
	}
};</pre><p><br/></p>]]></description><category>C/C++代码</category><comments>http://www.fenlog.com/post/135.html#comment</comments><wfw:commentRss>http://www.fenlog.com/feed.asp?cmt=135</wfw:commentRss></item><item><title>Java序列化解析类</title><author>345382462@qq.com (blackfeather)</author><link>http://www.fenlog.com/post/134.html</link><pubDate>Wed, 25 Jun 2025 14:57:49 +0800</pubDate><guid>http://www.fenlog.com/post/134.html</guid><description><![CDATA[<p><br/></p><p>java可以将任意对象序列化为一段内存流，也可以反序列化回对象。</p><p><br/></p><p>此代码用于dump序列化内容流，是用了Jsoncpp作为父类。<br/></p><p><br/></p><p>主要参考了<a href="https://github.com/NickstaDB/SerializationDumper这个项目。" _src="https://github.com/NickstaDB/SerializationDumper这个项目。">https://github.com/NickstaDB/SerializationDumper这个项目。</a> </p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p><br/></p><pre class="brush:cpp;toolbar:false">#pragma&nbsp;once

#include&nbsp;&lt;string&gt;
#include&nbsp;&lt;vector&gt;
#include&nbsp;&lt;cassert&gt;
#include&nbsp;&quot;FStream.h&quot;

#include&nbsp;&quot;json/json.h&quot;

//&nbsp;#ifdef&nbsp;_DEBUG
//&nbsp;#pragma&nbsp;comment(lib,&nbsp;&quot;jsoncpp/lib_json_mtd.lib&quot;)
//&nbsp;#else
//&nbsp;#pragma&nbsp;comment(lib,&nbsp;&quot;jsoncpp/lib_json_mt.lib&quot;)
//&nbsp;#endif&nbsp;//&nbsp;_DEBUG

/*
Java序列化内容（以0xACED开头）解析工具，转为jsoncpp的Json::Value
参考链接：&nbsp;https://github.com/NickstaDB/SerializationDumper

用法：
JavaSerializationHandler&nbsp;jsonSerialize;
jsonSerialize.OpenSerializationBuffer(buf);
jsonSerialize.OpenSerializationFile(filepath);
dumpjson(jsonSerialize);&nbsp;//直接就是Json::Value的子类，可以直接按照json使用

说明：
如果对象是Object，那么会该Object的json节点会出现几个元素(使用前还是要校验一下存在与否):
	&quot;_desc&quot;&nbsp;:&nbsp;类的描述信息，包括类名、字段描述、父类信息等等
	&quot;_data&quot;&nbsp;:&nbsp;类的数据
	&quot;_block&quot;:&nbsp;类的附加块数据，一些内置的java类型例如hashmap或者list等等会使用该字段，这里的数据一般需要二次整理，不要直接使用
	&quot;_array&quot;:&nbsp;如果是Array类型数据例如java.util.ArrayList等，会出现该字段，类型是数组，数组的元素由_block字段中整理并保存到该字段下
	&quot;_hashmap&quot;:如果是java.util.HashMap类型数据会出现该字段，由_block字段中的数据整理并保存到该字段下
	&quot;_enum&quot;&nbsp;:&nbsp;暂时还没碰到过这个类型的数据样本，围观。。。
	
实例：
	末尾部分。
*/


class&nbsp;JavaSerializationHandler&nbsp;:&nbsp;public&nbsp;Json::Value
{
	/*******************
	&nbsp;*&nbsp;Read&nbsp;a&nbsp;content&nbsp;element&nbsp;from&nbsp;the&nbsp;data&nbsp;stream.
	&nbsp;*
	&nbsp;*&nbsp;Could&nbsp;be&nbsp;any&nbsp;of:
	&nbsp;*	TC_OBJECT			(0x73)
	&nbsp;*	TC_CLASS			(0x76)
	&nbsp;*	TC_ARRAY			(0x75)
	&nbsp;*	TC_STRING			(0x74)
	&nbsp;*	TC_LONGSTRING		(0x7c)
	&nbsp;*	TC_ENUM				(0x7e)
	&nbsp;*	TC_CLASSDESC		(0x72)
	&nbsp;*	TC_PROXYCLASSDESC	(0x7d)
	&nbsp;*	TC_REFERENCE		(0x71)
	&nbsp;*	TC_NULL				(0x70)
	&nbsp;*	TC_EXCEPTION		(0x7b)
	&nbsp;*	TC_RESET			(0x79)
	&nbsp;*	TC_BLOCKDATA		(0x77)
	&nbsp;*	TC_BLOCKDATALONG	(0x7a)
	&nbsp;******************/

	enum&nbsp;classElementType
	{
		TC_NULL&nbsp;=&nbsp;0x70,
		TC_REFERENCE&nbsp;=&nbsp;0x71,
		TC_CLASSDESC&nbsp;=&nbsp;0x72,
		TC_OBJECT&nbsp;=&nbsp;0x73,
		TC_STRING&nbsp;=&nbsp;0x74,
		TC_ARRAY&nbsp;=&nbsp;0x75,
		TC_CLASS&nbsp;=&nbsp;0x76,
		TC_BLOCKDATA&nbsp;=&nbsp;0x77,
		TC_ENDBLOCKDATA&nbsp;=&nbsp;0x78,
		TC_RESET&nbsp;=&nbsp;0x79,
		TC_BLOCKDATALONG&nbsp;=&nbsp;0x7a,
		TC_EXCEPTION&nbsp;=&nbsp;0x7b,
		TC_LONGSTRING&nbsp;=&nbsp;0x7c,
		TC_ENUM&nbsp;=&nbsp;0x7e,
		TC_PROXYCLASSDESC&nbsp;=&nbsp;0x7d
	};

	enum&nbsp;classDescFlags
	{
		SC_WRITE_METHOD&nbsp;=&nbsp;0x01,
		SC_SERIALIZABLE&nbsp;=&nbsp;0x02,
		SC_EXTERNALIZABLE&nbsp;=&nbsp;0x04,
		SC_BLOCK_DATA&nbsp;=&nbsp;0x08
	};

	struct&nbsp;OBJECT_REF_DATA
	{
		uint32_t&nbsp;ref;
		uint8_t&nbsp;type;
		Json::Value&nbsp;data;
	};


#ifndef&nbsp;_bswap_16
	/*&nbsp;Swap&nbsp;bytes&nbsp;in&nbsp;16&nbsp;bit&nbsp;value.&nbsp;&nbsp;*/
#define&nbsp;_bswap_16(x)&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((unsigned&nbsp;short&nbsp;int)&nbsp;((((x)&nbsp;&gt;&gt;&nbsp;8)&nbsp;&amp;&nbsp;0xff)&nbsp;|&nbsp;(((x)&nbsp;&amp;&nbsp;0xff)&nbsp;&lt;&lt;&nbsp;8)))
#endif

#ifndef&nbsp;_bswap_32
/*&nbsp;Swap&nbsp;bytes&nbsp;in&nbsp;32&nbsp;bit&nbsp;value.&nbsp;&nbsp;*/
#define&nbsp;_bswap_32(x)&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((((x)&nbsp;&amp;&nbsp;0xff000000)&nbsp;&gt;&gt;&nbsp;24)&nbsp;|&nbsp;(((x)&nbsp;&amp;&nbsp;0x00ff0000)&nbsp;&gt;&gt;&nbsp;&nbsp;8)&nbsp;|		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(((x)&nbsp;&amp;&nbsp;0x0000ff00)&nbsp;&lt;&lt;&nbsp;&nbsp;8)&nbsp;|&nbsp;(((x)&nbsp;&amp;&nbsp;0x000000ff)&nbsp;&lt;&lt;&nbsp;24))
#endif

#ifndef&nbsp;_bswap_64
/*&nbsp;Swap&nbsp;bytes&nbsp;in&nbsp;64&nbsp;bit&nbsp;value.&nbsp;&nbsp;*/
#&nbsp;define&nbsp;_bswap_64(x)&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;((((x)&nbsp;&amp;&nbsp;0xff00000000000000ull)&nbsp;&gt;&gt;&nbsp;56)				&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(((x)&nbsp;&amp;&nbsp;0x00ff000000000000ull)&nbsp;&gt;&gt;&nbsp;40)				&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(((x)&nbsp;&amp;&nbsp;0x0000ff0000000000ull)&nbsp;&gt;&gt;&nbsp;24)				&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(((x)&nbsp;&amp;&nbsp;0x000000ff00000000ull)&nbsp;&gt;&gt;&nbsp;8)				&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(((x)&nbsp;&amp;&nbsp;0x00000000ff000000ull)&nbsp;&lt;&lt;&nbsp;8)				&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(((x)&nbsp;&amp;&nbsp;0x0000000000ff0000ull)&nbsp;&lt;&lt;&nbsp;24)				&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(((x)&nbsp;&amp;&nbsp;0x000000000000ff00ull)&nbsp;&lt;&lt;&nbsp;40)				&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;(((x)&nbsp;&amp;&nbsp;0x00000000000000ffull)&nbsp;&lt;&lt;&nbsp;56))
#endif

	
public:
	JavaSerializationHandler()&nbsp;:&nbsp;m_refIndex(0x7e0000)&nbsp;{};
	~JavaSerializationHandler()&nbsp;{};

	template&lt;typename&nbsp;_T&gt;
	bool&nbsp;OpenSerializationFile(const&nbsp;_T&amp;&nbsp;filepath)
	{
		FFileStream&nbsp;infile(filepath);
		m_bufstream&nbsp;=&nbsp;&amp;infile;

		bool&nbsp;ret&nbsp;=&nbsp;false;
#ifdef&nbsp;_DEBUG
		ret&nbsp;=&nbsp;JavaSerializationParser(*this);
#else
		try
		{
			ret&nbsp;=&nbsp;JavaSerializationParser(*this);
		}
		catch&nbsp;(...)&nbsp;{}
#endif&nbsp;//&nbsp;_DEBUG

		return&nbsp;ret;
	}

	bool&nbsp;OpenSerializationBuffer(const&nbsp;std::string&nbsp;&amp;buf)
	{
		FBufferStream&nbsp;stream(buf);
		m_bufstream&nbsp;=&nbsp;&amp;stream;
		
		bool&nbsp;ret&nbsp;=&nbsp;false;
#ifdef&nbsp;_DEBUG
		ret&nbsp;=&nbsp;JavaSerializationParser(*this);
#else
		try
		{
			ret&nbsp;=&nbsp;JavaSerializationParser(*this);
		}
		catch&nbsp;(...)&nbsp;{}
#endif&nbsp;//&nbsp;_DEBUG

		return&nbsp;ret;
	}
	
private:
	FStream*&nbsp;m_bufstream;
	std::vector&lt;OBJECT_REF_DATA&gt;&nbsp;m_objectRefs;
	uint32_t&nbsp;m_refIndex;
	

	const&nbsp;char*&nbsp;getTypeString(uint8_t&nbsp;type)
	{
		//获取enum类型的字符串
		switch&nbsp;(type)
		{
		case&nbsp;TC_NULL:
			return&nbsp;&quot;TC_NULL&quot;;
		case&nbsp;TC_REFERENCE:
			return&nbsp;&quot;TC_REFERENCE&quot;;
		case&nbsp;TC_CLASSDESC:
			return&nbsp;&quot;TC_CLASSDESC&quot;;
		case&nbsp;TC_OBJECT:
			return&nbsp;&quot;TC_OBJECT&quot;;
		case&nbsp;TC_STRING:
			return&nbsp;&quot;TC_STRING&quot;;
		case&nbsp;TC_ARRAY:
			return&nbsp;&quot;TC_ARRAY&quot;;
		case&nbsp;TC_CLASS:
			return&nbsp;&quot;TC_CLASS&quot;;
		case&nbsp;TC_BLOCKDATA:
			return&nbsp;&quot;TC_BLOCKDATA&quot;;
		case&nbsp;TC_ENDBLOCKDATA:
			return&nbsp;&quot;TC_ENDBLOCKDATA&quot;;
		case&nbsp;TC_RESET:
			return&nbsp;&quot;TC_RESET&quot;;
		case&nbsp;TC_BLOCKDATALONG:
			return&nbsp;&quot;TC_BLOCKDATALONG&quot;;
		case&nbsp;TC_EXCEPTION:
			return&nbsp;&quot;TC_EXCEPTION&quot;;
		case&nbsp;TC_LONGSTRING:
			return&nbsp;&quot;TC_LONGSTRING&quot;;
		case&nbsp;TC_ENUM:
			return&nbsp;&quot;TC_ENUM&quot;;
		}

		return&nbsp;&quot;UNKNOWN&quot;;
	}

	Json::Value&nbsp;getObjectByRef(uint32_t&nbsp;ref)
	{
		for&nbsp;(auto&amp;&nbsp;obj&nbsp;:&nbsp;m_objectRefs)
		{
			if&nbsp;(obj.ref&nbsp;==&nbsp;ref)
			{
#ifdef&nbsp;_DEBUG
//				if&nbsp;(obj.type&nbsp;==&nbsp;TC_STRING&nbsp;||&nbsp;obj.type&nbsp;==&nbsp;TC_LONGSTRING)
//					printf(&quot;get&nbsp;ref:0x%x,&nbsp;string:%s&nbsp;\n&quot;,&nbsp;ref,&nbsp;obj.data.asCString());
//				else
//					printf(&quot;get&nbsp;ref:0x%x,&nbsp;type:%s(%x)&nbsp;\n&quot;,&nbsp;ref,&nbsp;getTypeString(obj.type),&nbsp;obj.type);
#endif&nbsp;//&nbsp;_DEBUG

				return&nbsp;obj.data;
				break;
			}
		}

		return&nbsp;{};
	}

	uint32_t&nbsp;setObjectRef(uint8_t&nbsp;type,&nbsp;const&nbsp;Json::Value&amp;&nbsp;val)
	{
		m_objectRefs.emplace_back(OBJECT_REF_DATA{&nbsp;m_refIndex&nbsp;,&nbsp;type,&nbsp;val&nbsp;});
#ifdef&nbsp;_DEBUG
//&nbsp;		if&nbsp;(type&nbsp;==&nbsp;TC_STRING&nbsp;||&nbsp;type&nbsp;==&nbsp;TC_LONGSTRING)
//&nbsp;			printf(&quot;new&nbsp;ref:0x%x,&nbsp;string:%s&nbsp;\n&quot;,&nbsp;m_refIndex,&nbsp;val.asCString());
//&nbsp;		else
//&nbsp;			printf(&quot;new&nbsp;ref:0x%x,&nbsp;type:%s(%x)&nbsp;\n&quot;,&nbsp;m_refIndex,&nbsp;getTypeString(type),&nbsp;type);
#endif&nbsp;//&nbsp;_DEBUG
		return&nbsp;m_refIndex++;
	}

	void&nbsp;updateObjectRef(uint32_t&nbsp;ref,&nbsp;uint8_t&nbsp;type,&nbsp;const&nbsp;Json::Value&amp;&nbsp;val)
	{
		for&nbsp;(auto&amp;&nbsp;obj&nbsp;:&nbsp;m_objectRefs)
		{
			if&nbsp;(obj.ref&nbsp;==&nbsp;ref)
			{
				obj.type&nbsp;=&nbsp;type;
				obj.data&nbsp;=&nbsp;val;
				break;
			}
		}
	}

	std::string&nbsp;tohex(const&nbsp;std::string&amp;&nbsp;data)
	{
		std::string&nbsp;strRet;
		if&nbsp;(data.empty())
			return&nbsp;strRet;

		strRet.reserve(data.length()&nbsp;*&nbsp;2);
		const&nbsp;char*&nbsp;hex&nbsp;=&nbsp;&quot;0123456789abcdef&quot;;
		for&nbsp;(char&nbsp;ch&nbsp;:&nbsp;data)
		{
			strRet.push_back(hex[(unsigned&nbsp;char)ch&nbsp;&gt;&gt;&nbsp;4]);
			strRet.push_back(hex[(unsigned&nbsp;char)ch&nbsp;&amp;&nbsp;0xf]);
		}

		return&nbsp;strRet;
	}


	bool&nbsp;readReference(uint32_t&amp;&nbsp;ref)
	{
		if&nbsp;(!m_bufstream-&gt;read_data(ref))
			return&nbsp;false;

		ref&nbsp;=&nbsp;_bswap_32(ref);
		return&nbsp;true;
	}

	bool&nbsp;readUtf(std::string&amp;&nbsp;ret)
	{
		uint16_t&nbsp;len;
		if&nbsp;(!m_bufstream-&gt;read_data(len))
			return&nbsp;false;
		len&nbsp;=&nbsp;_bswap_16(len);

		if&nbsp;(len&nbsp;&amp;&amp;&nbsp;!m_bufstream-&gt;read_buffer(ret,&nbsp;len))
			return&nbsp;false;

		return&nbsp;true;
	}

	bool&nbsp;readLongUtf(std::string&amp;&nbsp;ret)
	{
		uint64_t&nbsp;len;
		if&nbsp;(!m_bufstream-&gt;read_data(len))
			return&nbsp;false;
		len&nbsp;=&nbsp;_bswap_64(len);

		if&nbsp;(!m_bufstream-&gt;read_buffer(ret,&nbsp;(size_t)len))
			return&nbsp;false;

		return&nbsp;true;
	}

	bool&nbsp;readNewString(Json::Value&amp;&nbsp;val)
	{
		uint8_t&nbsp;type;
		if&nbsp;(!m_bufstream-&gt;read_data(type))
			return&nbsp;false;

		if&nbsp;(type&nbsp;==&nbsp;TC_STRING&nbsp;||&nbsp;type&nbsp;==&nbsp;TC_LONGSTRING&nbsp;||&nbsp;type&nbsp;==&nbsp;TC_REFERENCE)
			return&nbsp;readContentElement(type,&nbsp;val);

		//剩下的类型都不对
		return&nbsp;false;
	}

	bool&nbsp;readNewArray(Json::Value&amp;&nbsp;jsonarray)
	{
		Json::Value&nbsp;jsonDesc;
		//Read&nbsp;the&nbsp;class&nbsp;data&nbsp;description&nbsp;to&nbsp;enable&nbsp;array&nbsp;elements&nbsp;to&nbsp;be&nbsp;read
		if&nbsp;(!readClassDesc(jsonDesc))
			return&nbsp;false;

		if&nbsp;(!jsonDesc.isObject()&nbsp;||&nbsp;!jsonDesc[&quot;classname&quot;].isString())
			return&nbsp;false;

		std::string&nbsp;str&nbsp;=&nbsp;jsonDesc[&quot;classname&quot;].asString();
		if&nbsp;(str.length()&nbsp;&lt;&nbsp;2&nbsp;||&nbsp;str[0]&nbsp;!=&nbsp;&#39;[&#39;)
			return&nbsp;false;
		uint8_t&nbsp;type&nbsp;=&nbsp;str[1];

		jsonarray[&quot;_desc&quot;]&nbsp;=&nbsp;jsonDesc;

		//索引值+1
		int&nbsp;refindex&nbsp;=&nbsp;setObjectRef(TC_ARRAY,&nbsp;jsonarray);

		//array&nbsp;size
		uint32_t&nbsp;arraysize;
		if&nbsp;(!m_bufstream-&gt;read_data(arraysize))
			return&nbsp;false;

		for&nbsp;(uint32_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;arraysize;&nbsp;i++)
		{
			if&nbsp;(!readFieldValue(type,&nbsp;jsonarray[&quot;_array&quot;][i]))
				return&nbsp;false;
		}

		//更新值
		updateObjectRef(refindex,&nbsp;TC_ARRAY,&nbsp;jsonarray);

		return&nbsp;true;
	}

	bool&nbsp;readFieldValue(int&nbsp;type,&nbsp;Json::Value&amp;&nbsp;jsonvalue)
	{
		switch&nbsp;(type)
		{
		case&nbsp;&#39;B&#39;:		//byte&nbsp;1
			uint8_t&nbsp;val;
			if&nbsp;(!m_bufstream-&gt;read_data(val))
				return&nbsp;false;
			jsonvalue&nbsp;=&nbsp;val;
			break;

		case&nbsp;&#39;C&#39;:		//char&nbsp;java里面的char占用2字节
		case&nbsp;&#39;S&#39;:		//short
		{
			uint16_t&nbsp;val;
			if&nbsp;(!m_bufstream-&gt;read_data(val))
				return&nbsp;false;
			jsonvalue&nbsp;=&nbsp;_bswap_16(val);
			break;
		}
		case&nbsp;&#39;D&#39;:		//double
		{
			uint64_t&nbsp;val;
			if&nbsp;(!m_bufstream-&gt;read_data(val))
				return&nbsp;false;
			val&nbsp;=&nbsp;_bswap_64(val);
			jsonvalue&nbsp;=&nbsp;*(double*)&amp;val;
			break;
		}
		case&nbsp;&#39;F&#39;:		//float
		{
			uint32_t&nbsp;val;
			if&nbsp;(!m_bufstream-&gt;read_data(val))
				return&nbsp;false;
			val&nbsp;=&nbsp;_bswap_32(val);
			jsonvalue&nbsp;=&nbsp;*(float*)&amp;val;
			break;
		}
		case&nbsp;&#39;I&#39;:		//int
		{
			uint32_t&nbsp;val;
			if&nbsp;(!m_bufstream-&gt;read_data(val))
				return&nbsp;false;
			jsonvalue&nbsp;=&nbsp;(int32_t)_bswap_32(val);
			break;
		}
		case&nbsp;&#39;J&#39;:		//long
		{
			uint64_t&nbsp;val;
			if&nbsp;(!m_bufstream-&gt;read_data(val))
				return&nbsp;false;
			jsonvalue&nbsp;=&nbsp;(int64_t)_bswap_64(val);
			break;
		}
		case&nbsp;&#39;Z&#39;:		//boolean
		{
			uint8_t&nbsp;val;
			if&nbsp;(!m_bufstream-&gt;read_data(val))
				return&nbsp;false;
			if&nbsp;(val)
				jsonvalue&nbsp;=&nbsp;true;
			else
				jsonvalue&nbsp;=&nbsp;false;
			break;
		}
		case&nbsp;&#39;[&#39;:		//array
		case&nbsp;&#39;L&#39;:		//object
			if&nbsp;(!readContentElement(jsonvalue))
				return&nbsp;false;
			break;

		default:		//Unknown&nbsp;field&nbsp;type
			printf(&quot;unknown&nbsp;filed&nbsp;type:&nbsp;%c\n&quot;,&nbsp;type);
			return&nbsp;false;
		}

		return&nbsp;true;
	}

	bool&nbsp;readClassAnnotation(Json::Value&amp;&nbsp;jsonret)
	{
		//classAnnotation
		int&nbsp;index&nbsp;=&nbsp;0;
		while&nbsp;(true)
		{
			uint8_t&nbsp;type;
			if&nbsp;(!m_bufstream-&gt;read_data(type))
				return&nbsp;false;

			if&nbsp;(type&nbsp;==&nbsp;TC_ENDBLOCKDATA)
				break;

			if&nbsp;(!readContentElement(type,&nbsp;jsonret[index]))
				return&nbsp;false;

			index++;
		}

		return&nbsp;true;
	}

	bool&nbsp;readNewClassDesc(Json::Value&amp;&nbsp;jsonret)
	{
		//由于desc很长很长，先占个坑把ref的index占了
		uint32_t&nbsp;refindex&nbsp;=&nbsp;setObjectRef(TC_CLASSDESC,&nbsp;jsonret);

		//className
		std::string&nbsp;className;
		if&nbsp;(!readUtf(className))
			return&nbsp;false;
		if&nbsp;(className.empty())
		{
			printf(&quot;Error:&nbsp;class&nbsp;name&nbsp;is&nbsp;empty.\n&quot;);
			return&nbsp;false;
		}
		jsonret[&quot;classname&quot;]&nbsp;=&nbsp;className;

		//serialVersionUID&nbsp;固定8字节
		std::string&nbsp;serialVersionUID;
		if&nbsp;(!m_bufstream-&gt;read_buffer(serialVersionUID,&nbsp;8))
			return&nbsp;false;
		jsonret[&quot;uid&quot;]&nbsp;=&nbsp;tohex(serialVersionUID);

		//classDescFlags
		uint8_t&nbsp;classflags;
		if&nbsp;(!m_bufstream-&gt;read_data(classflags))
			return&nbsp;false;
		////Validate&nbsp;classDescFlags
		if&nbsp;((classflags&nbsp;&amp;&nbsp;0x02)&nbsp;==&nbsp;0x02)
		{
			if&nbsp;((classflags&nbsp;&amp;&nbsp;0x04)&nbsp;==&nbsp;0x04)
			{
				printf(&quot;Error:&nbsp;Illegal&nbsp;classDescFlags,&nbsp;SC_SERIALIZABLE&nbsp;is&nbsp;not&nbsp;compatible&nbsp;with&nbsp;SC_EXTERNALIZABLE.&nbsp;\n&quot;);
				return&nbsp;false;
			}
			if&nbsp;((classflags&nbsp;&amp;&nbsp;0x08)&nbsp;==&nbsp;0x08)
			{
				printf(&quot;Error:&nbsp;Illegal&nbsp;classDescFlags,&nbsp;SC_SERIALIZABLE&nbsp;is&nbsp;not&nbsp;compatible&nbsp;with&nbsp;SC_BLOCK_DATA.&nbsp;\n&quot;);
				return&nbsp;false;
			}
		}
		else&nbsp;if&nbsp;((classflags&nbsp;&amp;&nbsp;0x04)&nbsp;==&nbsp;0x04)
		{
			if&nbsp;((classflags&nbsp;&amp;&nbsp;0x01)&nbsp;==&nbsp;0x01)
			{
				printf(&quot;Error:&nbsp;Illegal&nbsp;classDescFlags,&nbsp;SC_EXTERNALIZABLE&nbsp;is&nbsp;not&nbsp;compatible&nbsp;with&nbsp;SC_WRITE_METHOD.\n&quot;);
				return&nbsp;false;
			}
		}
		else&nbsp;if&nbsp;(classflags&nbsp;!=&nbsp;0x00)
		{
			printf(&quot;Error:&nbsp;Illegal&nbsp;classDescFlags,&nbsp;must&nbsp;include&nbsp;either&nbsp;SC_SERIALIZABLE&nbsp;or&nbsp;SC_EXTERNALIZABLE.&nbsp;\n&quot;);
			return&nbsp;false;
		}
		jsonret[&quot;flags&quot;]&nbsp;=&nbsp;classflags;

		//read&nbsp;fields
		uint16_t&nbsp;fieldcount;
		if&nbsp;(!m_bufstream-&gt;read_data(fieldcount))
			return&nbsp;false;
		fieldcount&nbsp;=&nbsp;_bswap_16(fieldcount);
		jsonret[&quot;fields&quot;].resize(0);
		for&nbsp;(uint16_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;fieldcount;&nbsp;i++)
		{
			uint8_t&nbsp;fieldtype;
			if&nbsp;(!m_bufstream-&gt;read_data(fieldtype))
				return&nbsp;false;
			jsonret[&quot;fields&quot;][i][&quot;type&quot;]&nbsp;=&nbsp;fieldtype;

			std::string&nbsp;fieldname;
			if&nbsp;(!readUtf(fieldname))
				return&nbsp;false;
			if&nbsp;(fieldname.empty())
			{
				printf(&quot;Error:&nbsp;field&nbsp;name&nbsp;is&nbsp;empty.\n&quot;);
				//return&nbsp;false;
			}
			jsonret[&quot;fields&quot;][i][&quot;name&quot;]&nbsp;=&nbsp;fieldname;

			if&nbsp;(fieldtype&nbsp;==&nbsp;&#39;[&#39;&nbsp;||&nbsp;fieldtype&nbsp;==&nbsp;&#39;L&#39;)
			{
				if&nbsp;(!readNewString(jsonret[&quot;fields&quot;][i][&quot;classname&quot;]))
					return&nbsp;false;
			}
		}

		//ClassAnnotation
		if&nbsp;(!readClassAnnotation(jsonret[&quot;annotation&quot;]))
			return&nbsp;false;

		//SuperClassDesc&nbsp;???
		if&nbsp;(!readClassDesc(jsonret[&quot;superclass&quot;]))
			return&nbsp;false;

		//更新refs
		updateObjectRef(refindex,&nbsp;TC_CLASSDESC,&nbsp;jsonret);

		return&nbsp;true;
	}

	bool&nbsp;readNewProxyClassDesc(Json::Value&amp;&nbsp;jsonret)
	{
		//由于desc很长很长，先占个坑把ref的index占了
		uint32_t&nbsp;refindex&nbsp;=&nbsp;setObjectRef(TC_PROXYCLASSDESC,&nbsp;jsonret);

		uint32_t&nbsp;count;
		if&nbsp;(!m_bufstream-&gt;read_data(count))
			return&nbsp;false;
		count&nbsp;=&nbsp;_bswap_32(count);

		//proxyInterfaceNames
		for&nbsp;(uint32_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;count;&nbsp;i++)
		{
			std::string&nbsp;classname;
			if&nbsp;(!readUtf(classname))
				return&nbsp;false;
			jsonret[&quot;proxyclass&quot;][i]&nbsp;=&nbsp;classname;
		}

		//ClassAnnotation
		if&nbsp;(!readClassAnnotation(jsonret[&quot;annotation&quot;]))
			return&nbsp;false;

		//SuperClassDesc&nbsp;???
		if&nbsp;(!readClassDesc(jsonret[&quot;superclass&quot;]))
			return&nbsp;false;

		//更新refs
		updateObjectRef(refindex,&nbsp;TC_PROXYCLASSDESC,&nbsp;jsonret);

		return&nbsp;true;
	}

	//读取class的描述信息
	bool&nbsp;readClassDesc(Json::Value&amp;&nbsp;jsonClassDesc)
	{
		uint8_t&nbsp;t;
		if&nbsp;(!m_bufstream-&gt;read_data(t))
			return&nbsp;false;

		if&nbsp;(t&nbsp;==&nbsp;TC_CLASSDESC&nbsp;||&nbsp;t&nbsp;==&nbsp;TC_PROXYCLASSDESC&nbsp;||&nbsp;t&nbsp;==&nbsp;TC_REFERENCE&nbsp;||&nbsp;t&nbsp;==&nbsp;TC_NULL)
			return&nbsp;readContentElement(t,&nbsp;jsonClassDesc);

		return&nbsp;false;
	}

	bool&nbsp;readClassData(const&nbsp;Json::Value&amp;&nbsp;classDesc,&nbsp;Json::Value&amp;&nbsp;jsonret)
	{
		//通过遍历递归的手法&nbsp;&nbsp;让superclass排到读取的前面去
		if&nbsp;(classDesc[&quot;superclass&quot;].isObject()&nbsp;&amp;&amp;&nbsp;classDesc[&quot;superclass&quot;][&quot;classname&quot;].isString())
		{
			//递归走起
			if&nbsp;(!readClassData(classDesc[&quot;superclass&quot;],&nbsp;jsonret[&quot;_super&quot;]))
				return&nbsp;false;
		}

		//搞自己的吧
		//仅支持标准TC_CLASSDESC&nbsp;&nbsp;TC_PROXYCLASSDESC暂无数据没办法测试研究。。。
		assert(classDesc[&quot;classname&quot;].isString());
		assert(classDesc[&quot;fields&quot;].isArray());
		assert(classDesc[&quot;flags&quot;].isInt());
		if&nbsp;(!classDesc[&quot;classname&quot;].isString())
		{
			printf(&quot;Error:no&nbsp;class&nbsp;name.\n&quot;);
			return&nbsp;false;
		}
		if&nbsp;(!classDesc[&quot;fields&quot;].isArray())
		{
			printf(&quot;Error:no&nbsp;fields.\n&quot;);
			return&nbsp;false;
		}
		if&nbsp;(!classDesc[&quot;flags&quot;].isInt())
		{
			printf(&quot;Error:no&nbsp;flags.\n&quot;);
			return&nbsp;false;
		}

		int&nbsp;flags&nbsp;=&nbsp;classDesc[&quot;flags&quot;].asInt();
		jsonret[&quot;_desc&quot;]&nbsp;=&nbsp;classDesc;

		if&nbsp;(flags&nbsp;&amp;&nbsp;SC_SERIALIZABLE)
		{
			//读取字段数据
			size_t&nbsp;fieldscount&nbsp;=&nbsp;classDesc[&quot;fields&quot;].size();
			for&nbsp;(size_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;fieldscount;&nbsp;i++)
			{
				const&nbsp;Json::Value&amp;&nbsp;jsonField&nbsp;=&nbsp;classDesc[&quot;fields&quot;][i];
				if&nbsp;(!readFieldValue(jsonField[&quot;type&quot;].asInt(),&nbsp;jsonret[&quot;_data&quot;][jsonField[&quot;name&quot;].asString()]))
					return&nbsp;false;
			}
		}

		bool&nbsp;hasBlockData&nbsp;=&nbsp;false;
		if&nbsp;((flags&nbsp;&amp;&nbsp;SC_SERIALIZABLE)&nbsp;&amp;&amp;&nbsp;(flags&nbsp;&amp;&nbsp;SC_WRITE_METHOD))
			hasBlockData&nbsp;=&nbsp;true;
		if&nbsp;(flags&nbsp;&amp;&nbsp;SC_EXTERNALIZABLE)
		{
			if&nbsp;(flags&nbsp;&amp;&nbsp;SC_BLOCK_DATA)
			{
				hasBlockData&nbsp;=&nbsp;true;
			}
			else
			{
				printf(&quot;Unable&nbsp;to&nbsp;parse&nbsp;externalContents&nbsp;for&nbsp;protocol&nbsp;version&nbsp;1.\n&quot;);
				return&nbsp;false;
			}
		}

		if&nbsp;(hasBlockData)
		{
			if&nbsp;(!readClassAnnotation(jsonret[&quot;_block&quot;]))
				return&nbsp;false;
		}

		//额外处理几种类型的显示
		//&quot;java.util.HashMap&quot;
		if&nbsp;(classDesc[&quot;classname&quot;].asString()&nbsp;==&nbsp;&quot;java.util.HashMap&quot;&nbsp;&amp;&amp;&nbsp;jsonret[&quot;_block&quot;].isArray())
		{
			jsonret[&quot;_hashmap&quot;].resize(0);

			//hashmap&nbsp;在block里面第0个疑似是hashmap的size()，直接无视，解析内容
			//内容是size；key-value；key-value；key-value；
			size_t&nbsp;blockcount&nbsp;=&nbsp;jsonret[&quot;_block&quot;].size();
			if&nbsp;(blockcount&nbsp;%&nbsp;2)&nbsp;//第一块是size&nbsp;&nbsp;后面是kv对
			{
				size_t&nbsp;kvcount&nbsp;=&nbsp;blockcount&nbsp;/&nbsp;2;
				//printf(&quot;Hashmap&nbsp;size:&nbsp;%d\n&quot;,&nbsp;kvcount);

				for&nbsp;(size_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;kvcount;&nbsp;i++)
				{
					//key
					Json::Value&nbsp;key;
					const&nbsp;Json::Value&amp;&nbsp;keytmp&nbsp;=&nbsp;jsonret[&quot;_block&quot;][i&nbsp;*&nbsp;2&nbsp;+&nbsp;1];
					if&nbsp;(keytmp.isObject()&nbsp;&amp;&amp;&nbsp;keytmp[&quot;_data&quot;].isObject()&nbsp;&amp;&amp;&nbsp;!keytmp[&quot;_data&quot;][&quot;value&quot;].isNull())
						key&nbsp;=&nbsp;keytmp[&quot;_data&quot;][&quot;value&quot;];
					else
						key&nbsp;=&nbsp;keytmp;

					//value
					const&nbsp;Json::Value&amp;&nbsp;valuetmp&nbsp;=&nbsp;jsonret[&quot;_block&quot;][i&nbsp;*&nbsp;2&nbsp;+&nbsp;2];
					if&nbsp;(valuetmp.isObject()&nbsp;&amp;&amp;&nbsp;valuetmp[&quot;_data&quot;].isObject()&nbsp;&amp;&amp;&nbsp;!valuetmp[&quot;_data&quot;][&quot;value&quot;].isNull())
						jsonret[&quot;_hashmap&quot;][i][key.asString()]&nbsp;=&nbsp;valuetmp[&quot;_data&quot;][&quot;value&quot;];
					else
						jsonret[&quot;_hashmap&quot;][i][key.asString()]&nbsp;=&nbsp;valuetmp;

#ifdef&nbsp;_DEBUG
					//printf(&quot;key:%s,&nbsp;value:%s\n&quot;,&nbsp;Json2String(key).c_str(),&nbsp;Json2String(jsonret[&quot;_hashmap&quot;][i][key.asString()]).c_str());
#endif&nbsp;//&nbsp;_DEBUG
				}
			}
		}

		//java.util.ArrayList
		if&nbsp;(classDesc[&quot;classname&quot;].asString()&nbsp;==&nbsp;&quot;java.util.ArrayList&quot;&nbsp;&amp;&amp;&nbsp;jsonret[&quot;_block&quot;].isArray())
		{
			jsonret[&quot;_array&quot;].resize(0);

			//_data里面有一个&quot;size&quot;:&nbsp;1表示的就是size
			//blockdata里面第一个元素应该也是长度，直接无视，读取所有blockdata
			size_t&nbsp;blockcount&nbsp;=&nbsp;jsonret[&quot;_block&quot;].size();
			if&nbsp;(blockcount&nbsp;&gt;&nbsp;1)
			{
				blockcount--;&nbsp;//将第一个元素去掉
				for&nbsp;(size_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;blockcount;&nbsp;i++)
				{
					Json::Value&amp;&nbsp;datatmp&nbsp;=&nbsp;jsonret[&quot;_block&quot;][i&nbsp;+&nbsp;1];
					if&nbsp;(datatmp.isObject()&nbsp;&amp;&amp;&nbsp;!datatmp[&quot;_data&quot;].isNull())
						jsonret[&quot;_array&quot;][i]&nbsp;=&nbsp;datatmp[&quot;_data&quot;];
					else
						jsonret[&quot;_array&quot;][i]&nbsp;=&nbsp;datatmp;
				}
			}
		}

		//其他的类型待补充
		//...

		return&nbsp;true;
	}

	bool&nbsp;readNewObject(Json::Value&amp;&nbsp;jsonret)
	{
		//printf(&quot;TC_OBJECT\n&quot;);

		//读取类描述
		Json::Value&nbsp;classDesc;
		if&nbsp;(!readClassDesc(classDesc))
			return&nbsp;false;

		//这中间要让索引值加1
		uint32_t&nbsp;refindex&nbsp;=&nbsp;setObjectRef(TC_OBJECT,&nbsp;jsonret);

		//读取值
		if&nbsp;(!readClassData(classDesc,&nbsp;jsonret))
			return&nbsp;false;

		//更新值
		updateObjectRef(refindex,&nbsp;TC_OBJECT,&nbsp;jsonret);

		return&nbsp;true;
	}

	bool&nbsp;readNewClass(Json::Value&amp;&nbsp;jsonret)
	{
		//printf(&quot;TC_CLASS\n&quot;);

		Json::Value&nbsp;classDesc;
		if&nbsp;(!readClassDesc(classDesc))
			return&nbsp;false;

		assert(classDesc[&quot;classname&quot;].isString());
		if&nbsp;(!classDesc[&quot;classname&quot;].isString())
		{
			//printf(&quot;Error:no&nbsp;class&nbsp;name.\n&quot;);
			return&nbsp;false;
		}

		jsonret[&quot;_desc&quot;]&nbsp;=&nbsp;classDesc;

		//增加引用&nbsp;不知道还能干啥
		m_refIndex++;

		return&nbsp;true;
	}


	bool&nbsp;readNewEnum(Json::Value&amp;&nbsp;jsonret)
	{
		//printf(&quot;TC_ENUM\n&quot;);

		Json::Value&nbsp;classDesc;
		if&nbsp;(!readClassDesc(classDesc))
			return&nbsp;false;

		assert(classDesc[&quot;classname&quot;].isString());
		if&nbsp;(!classDesc[&quot;classname&quot;].isString())
		{
			printf(&quot;Error:no&nbsp;class&nbsp;name.\n&quot;);
			return&nbsp;false;
		}

		//先增加引用
		int&nbsp;refindex&nbsp;=&nbsp;setObjectRef(TC_ENUM,&nbsp;jsonret);

		jsonret[&quot;_desc&quot;]&nbsp;=&nbsp;classDesc;

		//enumConstantName&nbsp;???
		if&nbsp;(!readNewString(jsonret[&quot;_enum&quot;]))
			return&nbsp;false;

		//更新引用的值
		updateObjectRef(refindex,&nbsp;TC_ENUM,&nbsp;jsonret);

		return&nbsp;true;
	}

	bool&nbsp;readBlockData(Json::Value&amp;&nbsp;jsonret)
	{
		//printf(&quot;TC_BLOCKDATA\n&quot;);

		uint8_t&nbsp;len;
		if&nbsp;(!m_bufstream-&gt;read_data(len))
			return&nbsp;false;

		if&nbsp;(len)
		{
			std::string&nbsp;block;
			if&nbsp;(!m_bufstream-&gt;read_buffer(block,&nbsp;len))
				return&nbsp;false;
			jsonret&nbsp;=&nbsp;tohex(block);
		}
		else
		{
			jsonret&nbsp;=&nbsp;&quot;&quot;;
		}

		return&nbsp;true;
	}

	bool&nbsp;readBlockDataLong(Json::Value&amp;&nbsp;jsonret)
	{
		//printf(&quot;TC_BLOCKDATALONG\n&quot;);

		uint32_t&nbsp;len;
		if&nbsp;(!m_bufstream-&gt;read_data(len))
			return&nbsp;false;

		len&nbsp;=&nbsp;_bswap_32(len);
		if&nbsp;(len)
		{
			std::string&nbsp;block;
			if&nbsp;(!m_bufstream-&gt;read_buffer(block,&nbsp;len))
				return&nbsp;false;
			jsonret&nbsp;=&nbsp;tohex(block);
		}
		else
		{
			jsonret&nbsp;=&nbsp;&quot;&quot;;
		}

		return&nbsp;true;
	}

	bool&nbsp;readContentElement(uint8_t&nbsp;type,&nbsp;Json::Value&amp;&nbsp;jsonret)
	{
		switch&nbsp;(type)
		{
		case&nbsp;TC_OBJECT:
			if&nbsp;(!readNewObject(jsonret))
				return&nbsp;false;
			break;
		case&nbsp;TC_CLASS:
			if&nbsp;(!readNewClass(jsonret))
				return&nbsp;false;
			break;
		case&nbsp;TC_ARRAY:
			if&nbsp;(!readNewArray(jsonret))
				return&nbsp;false;
			break;
		case&nbsp;TC_STRING:
		{
			std::string&nbsp;val;
			if&nbsp;(!readUtf(val))
				return&nbsp;false;
			jsonret&nbsp;=&nbsp;val;

			setObjectRef(TC_STRING,&nbsp;jsonret);
			break;
		}
		case&nbsp;TC_LONGSTRING:
		{
			std::string&nbsp;val;
			if&nbsp;(!readLongUtf(val))
				return&nbsp;false;
			jsonret&nbsp;=&nbsp;val;

			setObjectRef(TC_STRING,&nbsp;jsonret);
			break;
		}
		case&nbsp;TC_ENUM:
			if&nbsp;(!readNewEnum(jsonret))
				return&nbsp;false;
			break;
		case&nbsp;TC_CLASSDESC:
			if&nbsp;(!readNewClassDesc(jsonret))
				return&nbsp;false;
			break;
		case&nbsp;TC_PROXYCLASSDESC:
			if&nbsp;(!readNewProxyClassDesc(jsonret))
				return&nbsp;false;
			break;
		case&nbsp;TC_REFERENCE:
			uint32_t&nbsp;ref;
			if&nbsp;(!readReference(ref))
				return&nbsp;false;
			jsonret&nbsp;=&nbsp;getObjectByRef(ref);
			break;
		case&nbsp;TC_NULL:
			jsonret&nbsp;=&nbsp;NULL;
			break;
			//&nbsp;	case&nbsp;TC_EXCEPTION:
			//&nbsp;		printf(&quot;TC_EXCEPTION\n&quot;);
			//&nbsp;		break;
			//&nbsp;	case&nbsp;TC_RESET:
			//&nbsp;		printf(&quot;TC_RESET\n&quot;);
			//&nbsp;		break;
		case&nbsp;TC_BLOCKDATA:
			if&nbsp;(!readBlockData(jsonret))
				return&nbsp;false;
			break;
		case&nbsp;TC_BLOCKDATALONG:
			if&nbsp;(!readBlockDataLong(jsonret))
				return&nbsp;false;
			break;

		default:
			printf(&quot;Unknown&nbsp;type&nbsp;-&nbsp;%x\n&quot;,&nbsp;type);
			break;
		}

		return&nbsp;true;
	}
	bool&nbsp;readContentElement(Json::Value&amp;&nbsp;jsonret)
	{
		uint8_t&nbsp;t;
		if&nbsp;(!m_bufstream-&gt;read_data(t))
			return&nbsp;false;

		return&nbsp;readContentElement(t,&nbsp;jsonret);
	}

	bool&nbsp;JavaSerializationParser(Json::Value&amp;&nbsp;jsonret)
	{
		uint8_t&nbsp;b1,&nbsp;b2;

		//读取文件头
		if&nbsp;(!m_bufstream-&gt;read_data(b1))
			return&nbsp;false;
		if&nbsp;(b1&nbsp;!=&nbsp;0xac)
		{
			switch&nbsp;(b1)&nbsp;{
			case&nbsp;0x50:
				printf(&quot;RMI&nbsp;Call&nbsp;-&nbsp;0x50&nbsp;\n&quot;);
				break;
			case&nbsp;0x51:
				printf(&quot;RMI&nbsp;ReturnData&nbsp;-&nbsp;0x51&nbsp;\n&quot;);
				break;
			case&nbsp;0x52:
				printf(&quot;RMI&nbsp;Ping&nbsp;-&nbsp;0x52&nbsp;\n&quot;);
				break;
			case&nbsp;0x53:
				printf(&quot;RMI&nbsp;PingAck&nbsp;-&nbsp;0x53&nbsp;\n&quot;);
				break;
			case&nbsp;0x54:
				printf(&quot;RMI&nbsp;DgcAck&nbsp;-&nbsp;0x54&nbsp;\n&quot;);
				break;
			default:
				printf(&quot;Unknown&nbsp;RMI&nbsp;packet&nbsp;type&nbsp;-&nbsp;0x%x&nbsp;\n&quot;&nbsp;+&nbsp;b1);
				break;
			}

			//不是0xac开头的&nbsp;再读取一次
			if&nbsp;(!m_bufstream-&gt;read_data(b1))
				return&nbsp;false;
		}
		if&nbsp;(!m_bufstream-&gt;read_data(b2))
			return&nbsp;false;
		//校验文件头
		printf(&quot;STREAM_MAGIC&nbsp;-&nbsp;%x&nbsp;%x\n&quot;,&nbsp;b1,&nbsp;b2);
		if&nbsp;(b1&nbsp;!=&nbsp;0xac&nbsp;||&nbsp;b2&nbsp;!=&nbsp;0xed)
		{
			printf(&quot;Invalid&nbsp;STREAM_MAGIC,&nbsp;should&nbsp;be&nbsp;ac&nbsp;ed&nbsp;\n&quot;);
			return&nbsp;false;
		}

		//校验版本
		if&nbsp;(!m_bufstream-&gt;read_data(b1))
			return&nbsp;false;
		if&nbsp;(!m_bufstream-&gt;read_data(b2))
			return&nbsp;false;
		printf(&quot;STREAM_VERSION&nbsp;-&nbsp;%x&nbsp;%x\n&quot;,&nbsp;b1,&nbsp;b2);
		if&nbsp;(b1&nbsp;!=&nbsp;0x00&nbsp;||&nbsp;b2&nbsp;!=&nbsp;0x05)
		{
			printf(&quot;Invalid&nbsp;STREAM_VERSION,&nbsp;should&nbsp;be&nbsp;00&nbsp;05&nbsp;\n&quot;);
			return&nbsp;false;
		}

		//解析内容了
		int&nbsp;index&nbsp;=&nbsp;0;
		while&nbsp;(true)
		{
			Json::Value&nbsp;value;
			if&nbsp;(!readContentElement(value))
			{
				if&nbsp;(!value.isNull())
				{
					if&nbsp;(index&nbsp;==&nbsp;0)
						jsonret&nbsp;=&nbsp;value;
					else
						jsonret[index]&nbsp;=&nbsp;value;

					return&nbsp;false;
				}
			}

			if&nbsp;(m_bufstream-&gt;eof())
				break;

			//后面还有
			jsonret[index]&nbsp;=&nbsp;value;
			index++;
		}

		return&nbsp;true;
	}
};



/*
//实例1&nbsp;普通的对象&nbsp;使用_data
{
	&quot;_data&quot;:
	{
		&quot;B_points&quot;:&nbsp;0,
		&quot;account_desc&quot;:&nbsp;&quot;E会员&quot;,
		&quot;account_level&quot;:&nbsp;5,
		&quot;android_url&quot;:&nbsp;&quot;&quot;,
		&quot;auth_token&quot;:&nbsp;&quot;GgMBu55DmpL8hm7HNRsg&quot;,
		&quot;avatar&quot;:&nbsp;&quot;https://ugc.bthhotels.com/avatar/images/default_avatar.png!a160&quot;,
		&quot;balance&quot;:&nbsp;0,
		&quot;birth&quot;:&nbsp;&quot;1995/01/12&quot;,
		&quot;btn_text&quot;:&nbsp;&quot;&quot;,
		&quot;can_update_avatar&quot;:&nbsp;false,
		&quot;carbon_points&quot;:&nbsp;0,
		&quot;card_balance&quot;:&nbsp;&quot;0&quot;,
		&quot;card_code&quot;:&nbsp;&quot;&quot;,
		&quot;code&quot;:&nbsp;&quot;100000138749707&quot;,
		&quot;ctf_code&quot;:&nbsp;&quot;412702199601028052&quot;,
		&quot;ctf_type&quot;:&nbsp;&quot;ID&quot;,
		&quot;desc&quot;:&nbsp;&quot;&quot;,
		&quot;down_days_left&quot;:&nbsp;0,
		&quot;down_expire_date&quot;:&nbsp;&quot;2023-04-08&quot;,
		&quot;email&quot;:&nbsp;&quot;&quot;,
		&quot;gift_switch&quot;:&nbsp;true,
		&quot;growth_value&quot;:&nbsp;0,
		&quot;keep_level_growth_value&quot;:&nbsp;0,
		&quot;keep_level_score&quot;:&nbsp;&quot;&quot;,
		&quot;level_up_growth_value&quot;:&nbsp;1000,
		&quot;lvl_exp_text&quot;:&nbsp;&quot;&quot;,
		&quot;name&quot;:&nbsp;&quot;张三&quot;,
		&quot;new_balance&quot;:&nbsp;&quot;0&quot;,
		&quot;new_red_packet&quot;:&nbsp;&quot;0&quot;,
		&quot;next_account_desc&quot;:&nbsp;&quot;银会员&quot;,
		&quot;next_level_nights&quot;:&nbsp;3,
		&quot;nights&quot;:&nbsp;0,
		&quot;phone&quot;:&nbsp;&quot;136****7271&quot;,
		&quot;points&quot;:&nbsp;0,
		&quot;red_packet&quot;:&nbsp;0,
		&quot;score_speed_times&quot;:&nbsp;1.0,
		&quot;tags&quot;:&nbsp;&quot;[\&quot;SL01\&quot;,\&quot;E06\&quot;]&quot;,
		&quot;title&quot;:&nbsp;&quot;&quot;,
		&quot;user_markers&quot;:&nbsp;&quot;[]&quot;
	},
	&quot;_desc&quot;:
	{
		&quot;annotation&quot;:&nbsp;null,
		&quot;classname&quot;:&nbsp;&quot;com.ziipin.homeinn.model.UserInfo&quot;,
		&quot;fields&quot;:&nbsp;[
		{
			&quot;name&quot;:&nbsp;&quot;B_points&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;account_level&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;balance&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;can_update_avatar&quot;,
			&quot;type&quot;:&nbsp;90
		},
		{
			&quot;name&quot;:&nbsp;&quot;carbon_points&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;down_days_left&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;gift_switch&quot;,
			&quot;type&quot;:&nbsp;90
		},
		{
			&quot;name&quot;:&nbsp;&quot;growth_value&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;keep_level_growth_value&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;level_up_growth_value&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;next_level_nights&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;nights&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;points&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;red_packet&quot;,
			&quot;type&quot;:&nbsp;73
		},
		{
			&quot;name&quot;:&nbsp;&quot;score_speed_times&quot;,
			&quot;type&quot;:&nbsp;70
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;account_desc&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;android_url&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;auth_token&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;avatar&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;birth&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;btn_text&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;card_balance&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;card_code&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;code&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;ctf_code&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;ctf_type&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;desc&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;down_expire_date&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;email&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;keep_level_score&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;lvl_exp_text&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;name&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;new_balance&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;new_red_packet&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;next_account_desc&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;phone&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;tags&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;title&quot;,
			&quot;type&quot;:&nbsp;76
		},
		{
			&quot;classname&quot;:&nbsp;&quot;Ljava/lang/String;&quot;,
			&quot;name&quot;:&nbsp;&quot;user_markers&quot;,
			&quot;type&quot;:&nbsp;76
		}],
		&quot;flags&quot;:&nbsp;2,
		&quot;superclass&quot;:&nbsp;0,
		&quot;uid&quot;:&nbsp;&quot;fb74abdfc6ffaf03&quot;
	}
}



实例2：hashmap类的解析，看_hashmap字段
{
	&quot;_block&quot;:&nbsp;[
		&quot;0000000800000005&quot;,
		{
			&quot;_data&quot;:&nbsp;{
				&quot;value&quot;:&nbsp;98305
			},
			&quot;_desc&quot;:&nbsp;{
				&quot;annotation&quot;:&nbsp;null,
				&quot;classname&quot;:&nbsp;&quot;java.lang.Integer&quot;,
				&quot;fields&quot;:&nbsp;[
					{
						&quot;name&quot;:&nbsp;&quot;value&quot;,
						&quot;type&quot;:&nbsp;73
					}
				],
				&quot;flags&quot;:&nbsp;2,
				&quot;superclass&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				},
				&quot;uid&quot;:&nbsp;&quot;12e2a0a4f7818738&quot;
			},
			&quot;_super&quot;:&nbsp;{
				&quot;_desc&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				}
			}
		},
		{
			&quot;_data&quot;:&nbsp;{
				&quot;value&quot;:&nbsp;true
			},
			&quot;_desc&quot;:&nbsp;{
				&quot;annotation&quot;:&nbsp;null,
				&quot;classname&quot;:&nbsp;&quot;java.lang.Boolean&quot;,
				&quot;fields&quot;:&nbsp;[
					{
						&quot;name&quot;:&nbsp;&quot;value&quot;,
						&quot;type&quot;:&nbsp;90
					}
				],
				&quot;flags&quot;:&nbsp;2,
				&quot;superclass&quot;:&nbsp;0,
				&quot;uid&quot;:&nbsp;&quot;cd207280d59cfaee&quot;
			}
		},
		{
			&quot;_data&quot;:&nbsp;{
				&quot;value&quot;:&nbsp;256
			},
			&quot;_desc&quot;:&nbsp;{
				&quot;annotation&quot;:&nbsp;null,
				&quot;classname&quot;:&nbsp;&quot;java.lang.Integer&quot;,
				&quot;fields&quot;:&nbsp;[
					{
						&quot;name&quot;:&nbsp;&quot;value&quot;,
						&quot;type&quot;:&nbsp;73
					}
				],
				&quot;flags&quot;:&nbsp;2,
				&quot;superclass&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				},
				&quot;uid&quot;:&nbsp;&quot;12e2a0a4f7818738&quot;
			},
			&quot;_super&quot;:&nbsp;{
				&quot;_desc&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				}
			}
		},
		&quot;A86880902494769&quot;,
		{
			&quot;_data&quot;:&nbsp;{
				&quot;value&quot;:&nbsp;259
			},
			&quot;_desc&quot;:&nbsp;{
				&quot;annotation&quot;:&nbsp;null,
				&quot;classname&quot;:&nbsp;&quot;java.lang.Integer&quot;,
				&quot;fields&quot;:&nbsp;[
					{
						&quot;name&quot;:&nbsp;&quot;value&quot;,
						&quot;type&quot;:&nbsp;73
					}
				],
				&quot;flags&quot;:&nbsp;2,
				&quot;superclass&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				},
				&quot;uid&quot;:&nbsp;&quot;12e2a0a4f7818738&quot;
			},
			&quot;_super&quot;:&nbsp;{
				&quot;_desc&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				}
			}
		},
		&quot;OPPOOPPO&nbsp;R7sPlus:&nbsp;fp&nbsp;asimd&nbsp;evtstrm&nbsp;aes&nbsp;pmull&nbsp;sha1&nbsp;sha2&nbsp;crc32:&nbsp;AArch64&nbsp;Processor&nbsp;rev&nbsp;4&nbsp;(aarch64):&nbsp;8:&nbsp;Qualcomm&nbsp;Technologies,&nbsp;Inc&nbsp;MSM8939:&nbsp;null&quot;,
		{
			&quot;_data&quot;:&nbsp;{
				&quot;value&quot;:&nbsp;94209
			},
			&quot;_desc&quot;:&nbsp;{
				&quot;annotation&quot;:&nbsp;null,
				&quot;classname&quot;:&nbsp;&quot;java.lang.Integer&quot;,
				&quot;fields&quot;:&nbsp;[
					{
						&quot;name&quot;:&nbsp;&quot;value&quot;,
						&quot;type&quot;:&nbsp;73
					}
				],
				&quot;flags&quot;:&nbsp;2,
				&quot;superclass&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				},
				&quot;uid&quot;:&nbsp;&quot;12e2a0a4f7818738&quot;
			},
			&quot;_super&quot;:&nbsp;{
				&quot;_desc&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				}
			}
		},
		{
			&quot;_data&quot;:&nbsp;{
				&quot;value&quot;:&nbsp;0
			},
			&quot;_desc&quot;:&nbsp;{
				&quot;annotation&quot;:&nbsp;null,
				&quot;classname&quot;:&nbsp;&quot;java.lang.Integer&quot;,
				&quot;fields&quot;:&nbsp;[
					{
						&quot;name&quot;:&nbsp;&quot;value&quot;,
						&quot;type&quot;:&nbsp;73
					}
				],
				&quot;flags&quot;:&nbsp;2,
				&quot;superclass&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				},
				&quot;uid&quot;:&nbsp;&quot;12e2a0a4f7818738&quot;
			},
			&quot;_super&quot;:&nbsp;{
				&quot;_desc&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				}
			}
		},
		{
			&quot;_data&quot;:&nbsp;{
				&quot;value&quot;:&nbsp;258
			},
			&quot;_desc&quot;:&nbsp;{
				&quot;annotation&quot;:&nbsp;null,
				&quot;classname&quot;:&nbsp;&quot;java.lang.Integer&quot;,
				&quot;fields&quot;:&nbsp;[
					{
						&quot;name&quot;:&nbsp;&quot;value&quot;,
						&quot;type&quot;:&nbsp;73
					}
				],
				&quot;flags&quot;:&nbsp;2,
				&quot;superclass&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				},
				&quot;uid&quot;:&nbsp;&quot;12e2a0a4f7818738&quot;
			},
			&quot;_super&quot;:&nbsp;{
				&quot;_desc&quot;:&nbsp;{
					&quot;annotation&quot;:&nbsp;null,
					&quot;classname&quot;:&nbsp;&quot;java.lang.Number&quot;,
					&quot;fields&quot;:&nbsp;[],
					&quot;flags&quot;:&nbsp;2,
					&quot;superclass&quot;:&nbsp;0,
					&quot;uid&quot;:&nbsp;&quot;86ac951d0b94e08b&quot;
				}
			}
		},
		&quot;868809024947694&quot;
	],
	&quot;_data&quot;:&nbsp;{
		&quot;loadFactor&quot;:&nbsp;0.75
	},
	&quot;_desc&quot;:&nbsp;{
		&quot;annotation&quot;:&nbsp;null,
		&quot;classname&quot;:&nbsp;&quot;java.util.HashMap&quot;,
		&quot;fields&quot;:&nbsp;[
			{
				&quot;name&quot;:&nbsp;&quot;loadFactor&quot;,
				&quot;type&quot;:&nbsp;70
			}
		],
		&quot;flags&quot;:&nbsp;3,
		&quot;superclass&quot;:&nbsp;0,
		&quot;uid&quot;:&nbsp;&quot;0507dac1c31660d1&quot;
	},
	&quot;_hashmap&quot;:&nbsp;[
		{
			&quot;98305&quot;:&nbsp;true
		},
		{
			&quot;256&quot;:&nbsp;&quot;A86880902494769&quot;
		},
		{
			&quot;259&quot;:&nbsp;&quot;OPPOOPPO&nbsp;R7sPlus:&nbsp;fp&nbsp;asimd&nbsp;evtstrm&nbsp;aes&nbsp;pmull&nbsp;sha1&nbsp;sha2&nbsp;crc32:&nbsp;AArch64&nbsp;Processor&nbsp;rev&nbsp;4&nbsp;(aarch64):&nbsp;8:&nbsp;Qualcomm&nbsp;Technologies,&nbsp;Inc&nbsp;MSM8939:&nbsp;null&quot;
		},
		{
			&quot;94209&quot;:&nbsp;0
		},
		{
			&quot;258&quot;:&nbsp;&quot;868809024947694&quot;
		}
	]
}


*/</pre><p><br/></p>]]></description><category>C/C++代码</category><comments>http://www.fenlog.com/post/134.html#comment</comments><wfw:commentRss>http://www.fenlog.com/feed.asp?cmt=134</wfw:commentRss></item><item><title>zlib封装C++类</title><author>345382462@qq.com (blackfeather)</author><link>http://www.fenlog.com/post/133.html</link><pubDate>Wed, 25 Jun 2025 14:52:39 +0800</pubDate><guid>http://www.fenlog.com/post/133.html</guid><description><![CDATA[<p><br/></p><p>zlib封装C++类</p><pre class="brush:cpp;toolbar:false">#pragma&nbsp;once

#include&nbsp;&lt;string&gt;
#include&nbsp;&lt;memory&gt;

#include&nbsp;&lt;zlib.h&gt;

/*
	to&nbsp;(de-)compress&nbsp;deflate&nbsp;format,&nbsp;use&nbsp;wbits&nbsp;=&nbsp;-zlib.MAX_WBITS
	to&nbsp;(de-)compress&nbsp;zlib&nbsp;format,&nbsp;use&nbsp;wbits&nbsp;=&nbsp;zlib.MAX_WBITS,
			gzlib&nbsp;header:
				0x78&nbsp;0x01&nbsp;-&nbsp;No&nbsp;Compression/low
				0x78&nbsp;0x9C&nbsp;-&nbsp;Default&nbsp;Compression
				0x78&nbsp;0xDA&nbsp;-&nbsp;Best&nbsp;Compression
	to&nbsp;(de-)compress&nbsp;gzip&nbsp;format,&nbsp;use&nbsp;wbits&nbsp;=&nbsp;zlib.MAX_WBITS&nbsp;|&nbsp;16
			gzip&nbsp;header:0x1F&nbsp;0x8B
*/
class&nbsp;ZlibHandler
{
public:
	ZlibHandler()&nbsp;:&nbsp;
		m_nCacheSize&nbsp;(1024&nbsp;*&nbsp;1024),
		m_nErrCode(Z_OK),
		m_mode(0)
	{
		m_pCacheBuf&nbsp;=&nbsp;new&nbsp;char[m_nCacheSize];
		m_bFreeCacheBuf&nbsp;=&nbsp;true;
	}
	//使用外部缓冲区
	ZlibHandler(char&nbsp;*cachebuf,&nbsp;size_t&nbsp;cachesize)&nbsp;:
		m_pCacheBuf(cachebuf),
		m_nCacheSize(cachesize),
		m_bFreeCacheBuf(false),
		m_nErrCode(Z_OK),
		m_mode(0)&nbsp;{}
	~ZlibHandler()&nbsp;
	{
		if&nbsp;(m_mode&nbsp;==&nbsp;1)
			deflateEnd(&amp;m_zlibStream);
		else&nbsp;if&nbsp;(m_mode&nbsp;==&nbsp;2)
			inflateEnd(&amp;m_zlibStream);

		m_mode&nbsp;=&nbsp;0;

		if&nbsp;(m_bFreeCacheBuf)
			delete[]&nbsp;m_pCacheBuf;
	}

	/*
	*&nbsp;初始化压缩方法
	*&nbsp;不能同时初始化压缩和解压两个控制器
	*&nbsp;参数：
		windowBits&nbsp;=&nbsp;MAX_WBITS&nbsp;+&nbsp;16&nbsp;//默认是gzip，如果需要是zlib传入MAX_WBITS即可
	*/
	bool&nbsp;InitCompress(int&nbsp;windowBits&nbsp;=&nbsp;MAX_WBITS&nbsp;+&nbsp;16,&nbsp;int&nbsp;level&nbsp;=&nbsp;Z_DEFAULT_COMPRESSION,
		int&nbsp;method&nbsp;=&nbsp;Z_DEFLATED,&nbsp;int&nbsp;memLevel&nbsp;=&nbsp;8,&nbsp;int&nbsp;strategy&nbsp;=&nbsp;Z_DEFAULT_STRATEGY)
	{
		if&nbsp;(m_mode&nbsp;!=&nbsp;0)
			return&nbsp;false;

		if&nbsp;(deflateInit2(&amp;m_zlibStream,&nbsp;level,&nbsp;method,&nbsp;windowBits,&nbsp;memLevel,&nbsp;strategy)&nbsp;!=&nbsp;Z_OK)
			return&nbsp;false;

		m_windowbits&nbsp;=&nbsp;windowBits;
		m_mode&nbsp;=&nbsp;1;
		return&nbsp;true;
	}
	/*
	*&nbsp;销毁压缩控制器
	*&nbsp;一般情况下不需要主动调用，除非是上一次压缩数据结束了需要再次压缩新数据，先End再次Init（不推荐）
	*/
	void&nbsp;CompressEnd()
	{
		if&nbsp;(m_mode&nbsp;==&nbsp;1)
		{
			deflateEnd(&amp;m_zlibStream);
			m_mode&nbsp;=&nbsp;0;
		}
	}
	/*
	*&nbsp;压缩数据块，可以多次调用
	*&nbsp;结束的时候，需要调用CompressBlockFinish处理尾部数据
	*/
	inline&nbsp;std::string&nbsp;CompressBlock(const&nbsp;std::string&amp;&nbsp;src)
	{
		return&nbsp;CompressBlock(src.c_str(),&nbsp;src.size());
	}
	/*
	*&nbsp;压缩数据块，可以多次调用
	*&nbsp;结束的时候，需要调用CompressBlockFinish处理尾部数据
	*/
	std::string&nbsp;CompressBlock(const&nbsp;void*&nbsp;src,&nbsp;size_t&nbsp;srcLen)
	{
		if&nbsp;(src&nbsp;==&nbsp;NULL&nbsp;||&nbsp;srcLen&nbsp;==&nbsp;0)
			return&nbsp;{};

		m_zlibStream.next_in&nbsp;=&nbsp;(Bytef*)src;
		m_zlibStream.avail_in&nbsp;=&nbsp;(uint32_t)srcLen;

		std::string&nbsp;ret;
		do&nbsp;{
			m_zlibStream.avail_out&nbsp;=&nbsp;(uint32_t)m_nCacheSize;
			m_zlibStream.next_out&nbsp;=&nbsp;(Bytef*)m_pCacheBuf;
			m_nErrCode&nbsp;=&nbsp;deflate(&amp;m_zlibStream,&nbsp;Z_NO_FLUSH);
			if&nbsp;(m_nErrCode&nbsp;!=&nbsp;Z_OK)
				break;
			
			ret.append(m_pCacheBuf,&nbsp;m_nCacheSize&nbsp;-&nbsp;m_zlibStream.avail_out);
		}&nbsp;while&nbsp;(m_zlibStream.avail_out&nbsp;==&nbsp;0);

		return&nbsp;ret;
	}
	/*
	*&nbsp;完成压缩数据块
	*&nbsp;调用CompressBlock压缩数据完毕后，需要调用此方法，返回值也是内容的一部分
	*/
	std::string&nbsp;CompressBlockFinish()
	{
		if&nbsp;(m_nErrCode&nbsp;!=&nbsp;Z_OK)
			return&nbsp;{};
		
		std::string&nbsp;ret;
		do&nbsp;{
			m_zlibStream.avail_out&nbsp;=&nbsp;(uint32_t)m_nCacheSize;
			m_zlibStream.next_out&nbsp;=&nbsp;(Bytef*)m_pCacheBuf;
			m_nErrCode&nbsp;=&nbsp;deflate(&amp;m_zlibStream,&nbsp;Z_FINISH);
			if&nbsp;(m_nErrCode&nbsp;!=&nbsp;Z_STREAM_END&nbsp;&amp;&amp;&nbsp;m_nErrCode&nbsp;!=&nbsp;Z_OK)
				break;

			ret.append(m_pCacheBuf,&nbsp;m_nCacheSize&nbsp;-&nbsp;m_zlibStream.avail_out);
		}&nbsp;while&nbsp;(m_zlibStream.avail_out&nbsp;==&nbsp;0);
		
		return&nbsp;ret;
	}
	/*
	*&nbsp;压缩数据，一次性传入所有数据，返回压缩后的数据，不需要调用CompressBlockFinish
	*/
	std::string&nbsp;Compress(const&nbsp;std::string&amp;&nbsp;src)
	{
		return&nbsp;Compress(src.c_str(),&nbsp;src.size());
	}
	/*
	*&nbsp;压缩数据，一次性传入所有数据，返回压缩后的数据，不需要调用CompressBlockFinish
	*/
	std::string&nbsp;Compress(const&nbsp;void*&nbsp;src,&nbsp;size_t&nbsp;srcLen)
	{
		if&nbsp;(src&nbsp;==&nbsp;NULL&nbsp;||&nbsp;srcLen&nbsp;==&nbsp;0)
			return&nbsp;{};

		std::string&nbsp;ret&nbsp;=&nbsp;CompressBlock(src,&nbsp;srcLen);
		ret&nbsp;+=&nbsp;CompressBlockFinish();
		return&nbsp;ret;
	}
	
	
	/*
	*&nbsp;初始化解压控制器
	*&nbsp;不能同时初始化压缩和解压两个控制器
	*	windowBits：默认是MAX_WBITS&nbsp;+&nbsp;32，能自动检测zlib和gzip两种格式。如果是deflate格式，需要传入-MAX_WBITS
	*/
	bool&nbsp;InitDecompress(int&nbsp;windowBits&nbsp;=&nbsp;MAX_WBITS&nbsp;+&nbsp;32)
	{
		if&nbsp;(m_mode&nbsp;!=&nbsp;0)
			return&nbsp;false;

		if&nbsp;(inflateInit2(&amp;m_zlibStream,&nbsp;windowBits)&nbsp;!=&nbsp;Z_OK)
			return&nbsp;false;

		m_windowbits&nbsp;=&nbsp;windowBits;
		m_mode&nbsp;=&nbsp;2;
		return&nbsp;true;
	}
	/*
	*&nbsp;销毁解压控制器
	*&nbsp;一般情况下不需要主动调用，除非是上一次操作结束了需要再次解压新数据，先End再次Init（不推荐）
	*/
	void&nbsp;DecompressEnd()
	{
		if&nbsp;(m_mode&nbsp;==&nbsp;2)
		{
			inflateEnd(&amp;m_zlibStream);
			m_mode&nbsp;=&nbsp;0;
		}
	}
	/*
	*&nbsp;解压数据块，可以多次调用
	*/
	inline&nbsp;std::string&nbsp;Decompress(const&nbsp;std::string&amp;&nbsp;src)
	{
		return&nbsp;Decompress(src.c_str(),&nbsp;src.size());
	}
	/*
	*&nbsp;解压数据块，可以多次调用
	*/
	std::string&nbsp;Decompress(const&nbsp;void*&nbsp;src,&nbsp;size_t&nbsp;srcLen)
	{
		if&nbsp;(src&nbsp;==&nbsp;NULL&nbsp;||&nbsp;srcLen&nbsp;==&nbsp;0)
			return&nbsp;{};

		m_zlibStream.next_in&nbsp;=&nbsp;(Bytef*)src;
		m_zlibStream.avail_in&nbsp;=&nbsp;(uint32_t)srcLen;

		std::string&nbsp;ret;
		do&nbsp;{
			m_zlibStream.avail_out&nbsp;=&nbsp;(uint32_t)m_nCacheSize;
			m_zlibStream.next_out&nbsp;=&nbsp;(Bytef*)m_pCacheBuf;

			m_nErrCode&nbsp;=&nbsp;inflate(&amp;m_zlibStream,&nbsp;Z_NO_FLUSH);
			if&nbsp;(m_nErrCode&nbsp;!=&nbsp;Z_STREAM_END&nbsp;&amp;&amp;&nbsp;m_nErrCode&nbsp;!=&nbsp;Z_OK)
				break;

			ret.append(m_pCacheBuf,&nbsp;m_nCacheSize&nbsp;-&nbsp;m_zlibStream.avail_out);
		}&nbsp;while&nbsp;(m_zlibStream.avail_out&nbsp;==&nbsp;0);

		return&nbsp;ret;
	}

private:
	z_stream&nbsp;m_zlibStream{};
	char&nbsp;*m_pCacheBuf{};
	size_t&nbsp;m_nCacheSize;
	bool&nbsp;m_bFreeCacheBuf{};
	int&nbsp;m_nErrCode{};
	int&nbsp;m_windowbits{};
	int&nbsp;m_mode;&nbsp;//1-加密；2-解密
};

/*
void&nbsp;zlibhandlertest()
{
	std::string&nbsp;str&nbsp;=&nbsp;&quot;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx&quot;;
	for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;1024&nbsp;*&nbsp;1024;&nbsp;i++)
	{
		str.append(std::to_string(i));
	}
	char*&nbsp;pstr&nbsp;=&nbsp;strdup(str.c_str());
	char*&nbsp;pstr2&nbsp;=&nbsp;strdup(str.c_str());

	ZlibHandler&nbsp;z1;
	z1.InitCompress(-MAX_WBITS);
	std::string&nbsp;compress1&nbsp;=&nbsp;z1.Compress(str.c_str(),&nbsp;str.length());
	ZlibHandler&nbsp;z2;
	z2.InitCompress(-MAX_WBITS);
	std::string&nbsp;compress2_1&nbsp;=&nbsp;z2.CompressBlock(pstr,&nbsp;str.length());
	free(pstr);
	std::string&nbsp;compress2_2&nbsp;=&nbsp;z2.CompressBlock(pstr2,&nbsp;str.length());
	free(pstr2);
	std::string&nbsp;compress2_3&nbsp;=&nbsp;z2.CompressBlockFinish();

	ZlibHandler&nbsp;de1;
	de1.InitDecompress(-MAX_WBITS);
	std::string&nbsp;decompress1&nbsp;=&nbsp;de1.Decompress(compress1.c_str(),&nbsp;compress1.length());
	if&nbsp;(decompress1&nbsp;==&nbsp;str)
		printf(&quot;check&nbsp;ok!\n&quot;);
	ZlibHandler&nbsp;de2;
	de2.InitDecompress(-MAX_WBITS);
	std::string&nbsp;decompress2&nbsp;=&nbsp;de2.Decompress(compress2_1&nbsp;+&nbsp;compress2_2&nbsp;+&nbsp;compress2_3);
	if&nbsp;(decompress2&nbsp;==&nbsp;str&nbsp;+&nbsp;str)
		printf(&quot;check&nbsp;ok!\n&quot;);

	ZlibHandler&nbsp;de3;
	de3.InitDecompress(-MAX_WBITS);
	std::string&nbsp;decompress3&nbsp;=&nbsp;de3.Decompress(compress2_1);
	decompress3&nbsp;+=&nbsp;de3.Decompress(compress2_2);
	decompress3&nbsp;+=&nbsp;de3.Decompress(compress2_3);
	if&nbsp;(decompress3&nbsp;==&nbsp;str&nbsp;+&nbsp;str)
		printf(&quot;check&nbsp;ok!\n&quot;);
}
*/</pre><p><br/></p>]]></description><category>C/C++代码</category><comments>http://www.fenlog.com/post/133.html#comment</comments><wfw:commentRss>http://www.fenlog.com/feed.asp?cmt=133</wfw:commentRss></item><item><title>ECDH非对称加密C++</title><author>345382462@qq.com (blackfeather)</author><link>http://www.fenlog.com/post/132.html</link><pubDate>Mon, 16 Dec 2024 18:55:49 +0800</pubDate><guid>http://www.fenlog.com/post/132.html</guid><description><![CDATA[<p><br/></p><p>ECDH算法封装，基于openssl。</p><pre class="brush:cpp;toolbar:false">#pragma&nbsp;once

#include&nbsp;&lt;string&gt;

#include&nbsp;&lt;openssl/ec.h&gt;
#include&nbsp;&lt;openssl/ecdh.h&gt;
#include&nbsp;&lt;openssl/evp.h&gt;


class&nbsp;ECDHEncoder
{
private:
	EC_KEY*&nbsp;m_ecKey{&nbsp;nullptr&nbsp;};
	EC_KEY*&nbsp;m_ecPeerPubKey{&nbsp;nullptr&nbsp;};

public:
	ECDHEncoder()&nbsp;{};
	~ECDHEncoder()&nbsp;{
		if&nbsp;(m_ecKey)
			EC_KEY_free(m_ecKey);
		if&nbsp;(m_ecPeerPubKey)
			EC_KEY_free(m_ecPeerPubKey);
	};

	bool&nbsp;GenerateKey(int&nbsp;nid)
	{
		m_ecKey&nbsp;=&nbsp;EC_KEY_new_by_curve_name(nid);
		if&nbsp;(m_ecKey&nbsp;==&nbsp;nullptr)
			return&nbsp;false;

		if&nbsp;(1&nbsp;!=&nbsp;EC_KEY_generate_key(m_ecKey))
		{
			EC_KEY_free(m_ecKey);
			m_ecKey&nbsp;=&nbsp;nullptr;
			return&nbsp;false;
		}

		return&nbsp;true;
	}

	std::string&nbsp;GetMyEncodedPoint(point_conversion_form_t&nbsp;type&nbsp;=&nbsp;POINT_CONVERSION_UNCOMPRESSED)
	{
		const&nbsp;EC_GROUP*&nbsp;group&nbsp;=&nbsp;EC_KEY_get0_group(m_ecKey);
		const&nbsp;EC_POINT*&nbsp;pubKey&nbsp;=&nbsp;EC_KEY_get0_public_key(m_ecKey);

		size_t&nbsp;field_len&nbsp;=&nbsp;EC_GROUP_get_degree(group);
		size_t&nbsp;byte_len&nbsp;=&nbsp;(field_len&nbsp;+&nbsp;7)&nbsp;/&nbsp;8;

		unsigned&nbsp;char*&nbsp;point_data&nbsp;=&nbsp;(unsigned&nbsp;char*)malloc(byte_len&nbsp;*&nbsp;2&nbsp;+&nbsp;1);
		if&nbsp;(!point_data)
			return&nbsp;{};

		size_t&nbsp;point_data_len&nbsp;=&nbsp;EC_POINT_point2oct(group,&nbsp;pubKey,&nbsp;type,&nbsp;point_data,&nbsp;byte_len&nbsp;*&nbsp;2&nbsp;+&nbsp;1,&nbsp;nullptr);
		if&nbsp;(0&nbsp;==&nbsp;point_data_len)
		{
			free(point_data);
			return&nbsp;{};
		}

		std::string&nbsp;strPointData((char*)point_data,&nbsp;point_data_len);
		free(point_data);

		return&nbsp;strPointData;
	}

	std::string&nbsp;GetMyPublicKey()
	{
		BIO*&nbsp;out&nbsp;=&nbsp;BIO_new(BIO_s_mem());
		i2d_EC_PUBKEY_bio(out,&nbsp;m_ecKey);

		char*&nbsp;p;
		long&nbsp;length&nbsp;=&nbsp;BIO_get_mem_data(out,&nbsp;&amp;p);
		std::string&nbsp;strECPubKey(p,&nbsp;length);
		BIO_free_all(out);

		return&nbsp;strECPubKey;
	}

	bool&nbsp;SetPeerPublicKey(const&nbsp;std::string&amp;&nbsp;strPeerPublicKey)
	{
		if&nbsp;(m_ecPeerPubKey)
		{
			EC_KEY_free(m_ecPeerPubKey);
			m_ecPeerPubKey&nbsp;=&nbsp;nullptr;
		}

		const&nbsp;unsigned&nbsp;char*&nbsp;szBuf&nbsp;=&nbsp;(unsigned&nbsp;char*)strPeerPublicKey.c_str();
		m_ecPeerPubKey&nbsp;=&nbsp;d2i_EC_PUBKEY(nullptr,&nbsp;&amp;szBuf,&nbsp;(long)strPeerPublicKey.size());
		if&nbsp;(m_ecPeerPubKey&nbsp;==&nbsp;nullptr)
			return&nbsp;false;

		return&nbsp;true;
	}

	bool&nbsp;SetPeerEncodedPoint(const&nbsp;std::string&amp;&nbsp;strPointData)
	{
		if&nbsp;(m_ecPeerPubKey)
		{
			EC_KEY_free(m_ecPeerPubKey);
			m_ecPeerPubKey&nbsp;=&nbsp;nullptr;
		}

		const&nbsp;EC_GROUP*&nbsp;group&nbsp;=&nbsp;EC_KEY_get0_group(m_ecKey);
		EC_POINT*&nbsp;peerPubKey&nbsp;=&nbsp;EC_POINT_new(group);
		if&nbsp;(peerPubKey&nbsp;==&nbsp;nullptr)
			return&nbsp;false;

		const&nbsp;unsigned&nbsp;char*&nbsp;p&nbsp;=&nbsp;(const&nbsp;unsigned&nbsp;char*)strPointData.c_str();
		if&nbsp;(0&nbsp;==&nbsp;EC_POINT_oct2point(group,&nbsp;peerPubKey,&nbsp;p,&nbsp;strPointData.size(),&nbsp;nullptr))
		{
			EC_POINT_free(peerPubKey);
			return&nbsp;false;
		}

		m_ecPeerPubKey&nbsp;=&nbsp;EC_KEY_new_by_curve_name(EC_GROUP_get_curve_name(group));
		if&nbsp;(m_ecPeerPubKey&nbsp;==&nbsp;nullptr)
		{
			EC_POINT_free(peerPubKey);
			return&nbsp;false;
		}

		if&nbsp;(1&nbsp;!=&nbsp;EC_KEY_set_public_key(m_ecPeerPubKey,&nbsp;peerPubKey))
		{
			EC_POINT_free(peerPubKey);
			EC_KEY_free(m_ecPeerPubKey);
			m_ecPeerPubKey&nbsp;=&nbsp;nullptr;
			return&nbsp;false;
		}

		EC_POINT_free(peerPubKey);

		return&nbsp;true;
	}

	std::string&nbsp;ComputeSecretKey()
	{
		if&nbsp;(m_ecPeerPubKey&nbsp;==&nbsp;nullptr)
			return&nbsp;{};

		int&nbsp;field_size&nbsp;=&nbsp;EC_GROUP_get_degree(EC_KEY_get0_group(m_ecKey));
		size_t&nbsp;secret_len&nbsp;=&nbsp;(field_size&nbsp;+&nbsp;7)&nbsp;/&nbsp;8;
		unsigned&nbsp;char*&nbsp;secret&nbsp;=&nbsp;new&nbsp;unsigned&nbsp;char[secret_len];
		if&nbsp;(secret&nbsp;==&nbsp;nullptr)
			return&nbsp;{};

		secret_len&nbsp;=&nbsp;ECDH_compute_key(secret,&nbsp;secret_len,&nbsp;EC_KEY_get0_public_key(m_ecPeerPubKey),&nbsp;m_ecKey,&nbsp;nullptr);
		if&nbsp;(secret_len&nbsp;==&nbsp;0)
		{
			delete[]&nbsp;secret;
			return&nbsp;{};
		}
		std::string&nbsp;strSecretKey((const&nbsp;char*)secret,&nbsp;secret_len);
		delete[]&nbsp;secret;

		return&nbsp;strSecretKey;
	}
};</pre><p><br/></p>]]></description><category>C/C++代码</category><comments>http://www.fenlog.com/post/132.html#comment</comments><wfw:commentRss>http://www.fenlog.com/feed.asp?cmt=132</wfw:commentRss></item><item><title>openssl封装的RSA加解密方法</title><author>345382462@qq.com (blackfeather)</author><link>http://www.fenlog.com/post/131.html</link><pubDate>Tue, 19 Nov 2024 09:51:03 +0800</pubDate><guid>http://www.fenlog.com/post/131.html</guid><description><![CDATA[<p><br/></p><p>openssl使用rsa加解密，C++封装，支持设置公钥、私钥，公钥加解密，私钥加解密。<br/></p><p><br/></p><p>使用方法很简单:</p><pre class="brush:cpp;toolbar:false">CRSAEncoder&nbsp;mRSAEncoderPC;
//设置公钥
mRSAEncoderPC.SetPublicKey(&quot;-----BEGIN&nbsp;PUBLIC&nbsp;KEY-----\nMIGfMA0GCSqGSIb3DQE..........XApYoMnPE3g4xU4NceOBTFZtR5fp+w/MswIDAQAB\n-----END&nbsp;PUBLIC&nbsp;KEY-----&quot;);
//公钥解密
std::string&nbsp;strTest&nbsp;=&nbsp;mRSAEncoderPC.PublicDecrypt(&quot;......&quot;);
//公钥加密
strTest&nbsp;=&nbsp;mRSAEncoderPC.PublicEncrypt(&quot;......&quot;);




//另外一种
unsigned&nbsp;char&nbsp;d[]&nbsp;=&nbsp;{
	0x38,&nbsp;0x60,&nbsp;0x63,&nbsp;0x56,&nbsp;0xBC,&nbsp;0x54,&nbsp;0x52,&nbsp;0xBC,&nbsp;0xA6,&nbsp;0xB6,&nbsp;0xDB,&nbsp;0x47,&nbsp;0x49,&nbsp;0x08,&nbsp;0xE5,&nbsp;0xB8,
	0x0B,&nbsp;0xAA,&nbsp;0x44,&nbsp;0xEC,&nbsp;0x49,&nbsp;0x2B,&nbsp;0x35,&nbsp;0x5F,&nbsp;0xBB,&nbsp;0x1A,&nbsp;0xB2,&nbsp;0x29,&nbsp;0x2D,&nbsp;0x0F,&nbsp;0x2D,&nbsp;0xE2,
	0x93,&nbsp;0x70,&nbsp;0x4D,&nbsp;0x8F,&nbsp;0x6F,&nbsp;0x01,&nbsp;0x64,&nbsp;0xE4,&nbsp;0xC9,&nbsp;0x0C,&nbsp;0x03,&nbsp;0x4C,&nbsp;0x02,&nbsp;0x08,&nbsp;0xE6,&nbsp;0xB7,
	0xAF,&nbsp;0xBB,&nbsp;0x8A,&nbsp;0x0A,&nbsp;0xFF,&nbsp;0x84,&nbsp;0xB4,&nbsp;0xBA,&nbsp;0x9E,&nbsp;0x4B,&nbsp;0x1A,&nbsp;0xB2,&nbsp;0x0A,&nbsp;0x75,&nbsp;0xDA,&nbsp;0xFD,
	0x0E,&nbsp;0xBE,&nbsp;0x73,&nbsp;0xCA,&nbsp;0x5C,&nbsp;0xFC,&nbsp;0xA2,&nbsp;0x4D,&nbsp;0xCF,&nbsp;0x56,&nbsp;0xA8,&nbsp;0xAD,&nbsp;0x9D,&nbsp;0xC3,&nbsp;0x60,&nbsp;0x86,
	0xF5,&nbsp;0xA8,&nbsp;0xA0,&nbsp;0xD0,&nbsp;0xCD,&nbsp;0x7E,&nbsp;0x21,&nbsp;0x8A,&nbsp;0xCE,&nbsp;0x4C,&nbsp;0xCD,&nbsp;0x03,&nbsp;0xDE,&nbsp;0x76,&nbsp;0xF6,&nbsp;0xA5,
	0x95,&nbsp;0xA9,&nbsp;0x77,&nbsp;0x77,&nbsp;0xFF,&nbsp;0xF9,&nbsp;0xBA,&nbsp;0x3B,&nbsp;0x0F,&nbsp;0xD9,&nbsp;0xFF,&nbsp;0x50,&nbsp;0x63,&nbsp;0x6E,&nbsp;0xDD,&nbsp;0x49,
	0xFA,&nbsp;0x31,&nbsp;0x7D,&nbsp;0xE0,&nbsp;0xC5,&nbsp;0x81,&nbsp;0xC5,&nbsp;0x75,&nbsp;0x79,&nbsp;0xCE,&nbsp;0x1C,&nbsp;0x78,&nbsp;0x1A,&nbsp;0x94,&nbsp;0xD6,&nbsp;0x7A,
	0xA1,&nbsp;0xFD,&nbsp;0x24,&nbsp;0x9F,&nbsp;0x11,&nbsp;0x3A,&nbsp;0x1D,&nbsp;0xED,&nbsp;0xF4,&nbsp;0x5C,&nbsp;0x9E,&nbsp;0x03,&nbsp;0x7B,&nbsp;0x8D,&nbsp;0xFF,&nbsp;0xB7,
	0x04,&nbsp;0xC4,&nbsp;0x86,&nbsp;0x24,&nbsp;0x3D,&nbsp;0xD2,&nbsp;0x9F,&nbsp;0xAB,&nbsp;0xB6,&nbsp;0x2B,&nbsp;0x09,&nbsp;0x55,&nbsp;0x97,&nbsp;0x66,&nbsp;0x7B,&nbsp;0xAA,
	0xF5,&nbsp;0x0E,&nbsp;0x25,&nbsp;0xA0,&nbsp;0x82,&nbsp;0x4B,&nbsp;0x02,&nbsp;0x70,&nbsp;0x84,&nbsp;0xCB,&nbsp;0x5F,&nbsp;0xA1,&nbsp;0x55,&nbsp;0xBB,&nbsp;0x63,&nbsp;0x56,
	0xC3,&nbsp;0x76,&nbsp;0xB8,&nbsp;0xFB,&nbsp;0x5D,&nbsp;0x38,&nbsp;0x62,&nbsp;0xF0,&nbsp;0x10,&nbsp;0xD6,&nbsp;0x03,&nbsp;0x0C,&nbsp;0x6A,&nbsp;0xC3,&nbsp;0x53,&nbsp;0xE9,
	0x55,&nbsp;0xA2,&nbsp;0x9D,&nbsp;0x2B,&nbsp;0x79,&nbsp;0x05,&nbsp;0x21,&nbsp;0xFF,&nbsp;0x70,&nbsp;0x8A,&nbsp;0x2F,&nbsp;0xE3,&nbsp;0x4C,&nbsp;0xF7,&nbsp;0x3D,&nbsp;0x90,
	0x95,&nbsp;0xB9,&nbsp;0x3C,&nbsp;0x53,&nbsp;0x61,&nbsp;0xC7,&nbsp;0xB8,&nbsp;0x72,&nbsp;0x91,&nbsp;0xB5,&nbsp;0x3D,&nbsp;0x7F,&nbsp;0x57,&nbsp;0x8D,&nbsp;0x4C,&nbsp;0xCB,
	0xF3,&nbsp;0x93,&nbsp;0x2C,&nbsp;0x14,&nbsp;0x13,&nbsp;0xF6,&nbsp;0x50,&nbsp;0xDD,&nbsp;0x3F,&nbsp;0x70,&nbsp;0xDE,&nbsp;0x7E,&nbsp;0x26,&nbsp;0x34,&nbsp;0xF4,&nbsp;0xCA,
	0x69,&nbsp;0xBF,&nbsp;0xAF,&nbsp;0x10,&nbsp;0xE8,&nbsp;0xD3,&nbsp;0xDD,&nbsp;0xEA,&nbsp;0x95,&nbsp;0x22,&nbsp;0x22,&nbsp;0xA3,&nbsp;0x06,&nbsp;0x73,&nbsp;0x0E,&nbsp;0xC1
};


unsigned&nbsp;char&nbsp;n[]&nbsp;=&nbsp;{
	0xCA,&nbsp;0x76,&nbsp;0x88,&nbsp;0xB4,&nbsp;0xCA,&nbsp;0x54,&nbsp;0x3D,&nbsp;0x75,&nbsp;0x00,&nbsp;0x50,&nbsp;0xD4,&nbsp;0x87,&nbsp;0x59,&nbsp;0x1F,&nbsp;0x9D,&nbsp;0xB4,
	0x2E,&nbsp;0xE2,&nbsp;0xF4,&nbsp;0xB7,&nbsp;0x11,&nbsp;0xA0,&nbsp;0x55,&nbsp;0xE4,&nbsp;0xC3,&nbsp;0x96,&nbsp;0x30,&nbsp;0x73,&nbsp;0x94,&nbsp;0xC6,&nbsp;0x10,&nbsp;0x19,
	0xD4,&nbsp;0x94,&nbsp;0xC4,&nbsp;0xC8,&nbsp;0x8D,&nbsp;0xFA,&nbsp;0x05,&nbsp;0xC8,&nbsp;0x39,&nbsp;0x22,&nbsp;0x46,&nbsp;0x8F,&nbsp;0xDD,&nbsp;0x0D,&nbsp;0xF7,&nbsp;0xF4,
	0xC1,&nbsp;0x77,&nbsp;0x31,&nbsp;0xB5,&nbsp;0x96,&nbsp;0xA4,&nbsp;0xF2,&nbsp;0x57,&nbsp;0x53,&nbsp;0x5D,&nbsp;0x91,&nbsp;0x55,&nbsp;0x76,&nbsp;0x36,&nbsp;0xC2,&nbsp;0x1B,
	0x44,&nbsp;0x5A,&nbsp;0x35,&nbsp;0x67,&nbsp;0x13,&nbsp;0x6A,&nbsp;0x39,&nbsp;0xB0,&nbsp;0xA6,&nbsp;0xD4,&nbsp;0x5B,&nbsp;0xCD,&nbsp;0xDE,&nbsp;0x99,&nbsp;0x4D,&nbsp;0xCA,
	0x78,&nbsp;0x9B,&nbsp;0xBF,&nbsp;0x52,&nbsp;0x79,&nbsp;0xD5,&nbsp;0x6C,&nbsp;0xCD,&nbsp;0x33,&nbsp;0xA9,&nbsp;0x04,&nbsp;0x09,&nbsp;0x15,&nbsp;0x3C,&nbsp;0x7D,&nbsp;0xB3,
	0x36,&nbsp;0xD2,&nbsp;0xA2,&nbsp;0x7E,&nbsp;0xAA,&nbsp;0xA2,&nbsp;0x81,&nbsp;0x52,&nbsp;0x9C,&nbsp;0xEF,&nbsp;0x15,&nbsp;0x98,&nbsp;0x42,&nbsp;0x17,&nbsp;0x19,&nbsp;0xB9,
	0xB6,&nbsp;0x2D,&nbsp;0x24,&nbsp;0xC5,&nbsp;0x82,&nbsp;0x08,&nbsp;0xE1,&nbsp;0x1D,&nbsp;0x0A,&nbsp;0xC0,&nbsp;0xF9,&nbsp;0xAD,&nbsp;0x22,&nbsp;0xE6,&nbsp;0xB8,&nbsp;0xDC,
	0xDA,&nbsp;0x8B,&nbsp;0xCE,&nbsp;0x06,&nbsp;0x71,&nbsp;0x9D,&nbsp;0x64,&nbsp;0x14,&nbsp;0xEF,&nbsp;0xD3,&nbsp;0x26,&nbsp;0x7F,&nbsp;0x76,&nbsp;0xB2,&nbsp;0x87,&nbsp;0xF3,
	0x0D,&nbsp;0x75,&nbsp;0x5C,&nbsp;0x57,&nbsp;0x02,&nbsp;0xBE,&nbsp;0xA4,&nbsp;0x18,&nbsp;0xFB,&nbsp;0x76,&nbsp;0xED,&nbsp;0xEF,&nbsp;0xCA,&nbsp;0x60,&nbsp;0x83,&nbsp;0xBE,
	0xE3,&nbsp;0xC0,&nbsp;0x42,&nbsp;0x70,&nbsp;0x56,&nbsp;0x05,&nbsp;0xDB,&nbsp;0x5D,&nbsp;0xCA,&nbsp;0xF5,&nbsp;0xE6,&nbsp;0xF6,&nbsp;0xA2,&nbsp;0x91,&nbsp;0xFD,&nbsp;0x53,
	0x03,&nbsp;0xA9,&nbsp;0x86,&nbsp;0x39,&nbsp;0x0B,&nbsp;0xB8,&nbsp;0xC4,&nbsp;0x25,&nbsp;0x1D,&nbsp;0x31,&nbsp;0x55,&nbsp;0x05,&nbsp;0xFC,&nbsp;0x8A,&nbsp;0xB4,&nbsp;0x3E,
	0x01,&nbsp;0x58,&nbsp;0x3C,&nbsp;0x6D,&nbsp;0x2D,&nbsp;0x5D,&nbsp;0xE1,&nbsp;0x0D,&nbsp;0xE1,&nbsp;0x7A,&nbsp;0x0E,&nbsp;0xD9,&nbsp;0x6D,&nbsp;0x08,&nbsp;0x8D,&nbsp;0xDE,
	0xDD,&nbsp;0x93,&nbsp;0xA3,&nbsp;0x2E,&nbsp;0xA4,&nbsp;0xE2,&nbsp;0xC7,&nbsp;0xAE,&nbsp;0xC7,&nbsp;0xC5,&nbsp;0x83,&nbsp;0xC4,&nbsp;0xE1,&nbsp;0x4D,&nbsp;0xFC,&nbsp;0x67,
	0x92,&nbsp;0x75,&nbsp;0x99,&nbsp;0xF4,&nbsp;0x3A,&nbsp;0x5F,&nbsp;0x98,&nbsp;0xE7,&nbsp;0x21,&nbsp;0xD9,&nbsp;0x15,&nbsp;0x14,&nbsp;0xFC,&nbsp;0x45,&nbsp;0x34,&nbsp;0x04,
	0x6D,&nbsp;0xD1,&nbsp;0x6E,&nbsp;0xF7,&nbsp;0x2D,&nbsp;0x96,&nbsp;0xB3,&nbsp;0xD3,&nbsp;0xAE,&nbsp;0x43,&nbsp;0xC3,&nbsp;0x4D,&nbsp;0x26,&nbsp;0x23,&nbsp;0x5E,&nbsp;0x7F
};

unsigned&nbsp;char&nbsp;e[]&nbsp;=&nbsp;{&nbsp;0x01,&nbsp;0x00,&nbsp;0x01&nbsp;};

CRSAEncoder&nbsp;mRSAEncoder;
//设置私钥
mRSAEncoder.SetPrivateKey(n,&nbsp;sizeof(n),&nbsp;e,&nbsp;sizeof(e),&nbsp;d,&nbsp;sizeof(d));
//设置私钥另外一个姿势也可以的
//mRSAEncoder.SetPrivateKey(&quot;-----BEGIN&nbsp;PRIVATE&nbsp;KEY-----\nMIICeAIBA..............DANBQltd+11\n-----END&nbsp;PRIVATE&nbsp;KEY-----&quot;);
//私钥加密
FString&nbsp;strEnBuf&nbsp;=&nbsp;mRSAEncoder.PrivateEncrypt(&quot;......&quot;);
//私钥解密
strEnBuf&nbsp;=&nbsp;mRSAEncoder.PrivateDecrypt(&quot;......&quot;);</pre><p><br/></p><p><br style="text-wrap-mode: wrap;"/></p><p><br/></p><p>RSAEncoder.h（header only）:</p><pre class="brush:cpp;toolbar:false">#pragma&nbsp;once

#include&nbsp;&lt;string&gt;
#include&nbsp;&lt;memory&gt;

#include&nbsp;&lt;openssl/rsa.h&gt;
#include&nbsp;&lt;openssl/pem.h&gt;
#include&nbsp;&lt;openssl/err.h&gt;

class&nbsp;CRSAEncoder
{
public:

	CRSAEncoder(void)
	{
		m_pRSAPrivate&nbsp;=&nbsp;NULL;
		m_pRSAPublic&nbsp;=&nbsp;NULL;

		m_nPaddingType&nbsp;=&nbsp;RSA_PKCS1_PADDING;
		m_nPaddingLen&nbsp;=&nbsp;RSA_PKCS1_PADDING_SIZE;
	}

	~CRSAEncoder(void)
	{
		if&nbsp;(m_pRSAPrivate)
			RSA_free(m_pRSAPrivate);
		if&nbsp;(m_pRSAPublic)
			RSA_free(m_pRSAPublic);
	}

	//设置padding类型，缺省为RSA_PKCS1_PADDING
	void&nbsp;SetPaddingType(int&nbsp;nPadding)
	{
		m_nPaddingType&nbsp;=&nbsp;nPadding;
	}

	//设置padding长度，缺省为RSA_PKCS1_PADDING_SIZE
	void&nbsp;SetPaddingLength(int&nbsp;nPaddingLen)
	{
		m_nPaddingLen&nbsp;=&nbsp;nPaddingLen;
	}

	//设置公钥，-----BEGIN&nbsp;PUBLIC&nbsp;KEY-----...-----END&nbsp;PUBLIC&nbsp;KEY-----
	bool&nbsp;SetPublicKey(const&nbsp;char*&nbsp;szKeyData)
	{
		if&nbsp;(m_pRSAPublic)
		{
			RSA_free(m_pRSAPublic);
			m_pRSAPublic&nbsp;=&nbsp;NULL;
		}

		BIO*&nbsp;pKEY&nbsp;=&nbsp;NULL;
		try
		{
			pKEY&nbsp;=&nbsp;_GetBIOFromKeyString(szKeyData);
			if&nbsp;(pKEY&nbsp;==&nbsp;NULL)
				throw&nbsp;ERR_get_error();

			m_pRSAPublic&nbsp;=&nbsp;PEM_read_bio_RSA_PUBKEY(pKEY,&nbsp;NULL,&nbsp;NULL,&nbsp;NULL);

			BIO_free_all(pKEY);

			if&nbsp;(m_pRSAPublic&nbsp;==&nbsp;NULL)
				throw&nbsp;ERR_get_error();
		}
		catch&nbsp;(unsigned&nbsp;long&nbsp;e)
		{
			printf(&quot;rsa&nbsp;set&nbsp;public&nbsp;key&nbsp;err&nbsp;%d.\n&quot;,&nbsp;e);
			return&nbsp;false;
		}

		return&nbsp;true;
	}
	//设置公钥，n,e，n和e为原始内存数据
	void&nbsp;SetPublicKey(const&nbsp;void*&nbsp;n,&nbsp;int&nbsp;nlen,&nbsp;const&nbsp;void*&nbsp;e,&nbsp;int&nbsp;elen)
	{
		if&nbsp;(m_pRSAPublic)
			RSA_free(m_pRSAPublic);

		m_pRSAPublic&nbsp;=&nbsp;RSA_new();
		BIGNUM*&nbsp;bn&nbsp;=&nbsp;BN_new();
		BIGNUM*&nbsp;be&nbsp;=&nbsp;BN_new();
		BN_bin2bn((const&nbsp;unsigned&nbsp;char*)n,&nbsp;nlen,&nbsp;bn);
		BN_bin2bn((const&nbsp;unsigned&nbsp;char*)e,&nbsp;elen,&nbsp;be);
		RSA_set0_key(m_pRSAPublic,&nbsp;bn,&nbsp;be,&nbsp;NULL);
	}
	void&nbsp;SetPublicKey(const&nbsp;std::string&amp;&nbsp;n,&nbsp;const&nbsp;std::string&amp;&nbsp;e)
	{
		SetPublicKey(n.c_str(),&nbsp;(int)n.length(),&nbsp;e.c_str(),&nbsp;(int)e.length());
	}
	//设置私钥，-----BEGIN&nbsp;PRIVATE&nbsp;KEY-----...-----END&nbsp;PRIVATE&nbsp;KEY-----
	bool&nbsp;SetPrivateKey(const&nbsp;char*&nbsp;szKeyData)
	{
		if&nbsp;(m_pRSAPrivate)
		{
			RSA_free(m_pRSAPrivate);
			m_pRSAPrivate&nbsp;=&nbsp;NULL;
		}

		BIO*&nbsp;pKEY&nbsp;=&nbsp;NULL;
		try
		{
			pKEY&nbsp;=&nbsp;_GetBIOFromKeyString(szKeyData);
			if&nbsp;(pKEY&nbsp;==&nbsp;NULL)
				throw&nbsp;ERR_get_error();

			m_pRSAPrivate&nbsp;=&nbsp;PEM_read_bio_RSAPrivateKey(pKEY,&nbsp;NULL,&nbsp;NULL,&nbsp;NULL);

			BIO_free_all(pKEY);

			if&nbsp;(m_pRSAPrivate&nbsp;==&nbsp;NULL)
				throw&nbsp;ERR_get_error();
		}
		catch&nbsp;(unsigned&nbsp;long&nbsp;e)
		{
			printf(&quot;rsa&nbsp;set&nbsp;private&nbsp;key&nbsp;err&nbsp;%d.\n&quot;,&nbsp;e);
			return&nbsp;false;
		}

		return&nbsp;true;
	}
	//设置私钥，n,e,d
	void&nbsp;SetPrivateKey(const&nbsp;void*&nbsp;n,&nbsp;int&nbsp;nlen,&nbsp;const&nbsp;void*&nbsp;e,&nbsp;int&nbsp;elen,&nbsp;const&nbsp;void*&nbsp;d,&nbsp;int&nbsp;dlen)
	{
		if&nbsp;(m_pRSAPrivate)
			RSA_free(m_pRSAPrivate);

		m_pRSAPrivate&nbsp;=&nbsp;RSA_new();
		BIGNUM*&nbsp;bn&nbsp;=&nbsp;BN_new();
		BIGNUM*&nbsp;be&nbsp;=&nbsp;BN_new();
		BIGNUM*&nbsp;bd&nbsp;=&nbsp;BN_new();
		BN_bin2bn((const&nbsp;unsigned&nbsp;char*)n,&nbsp;nlen,&nbsp;bn);
		BN_bin2bn((const&nbsp;unsigned&nbsp;char*)e,&nbsp;elen,&nbsp;be);
		BN_bin2bn((const&nbsp;unsigned&nbsp;char*)d,&nbsp;dlen,&nbsp;bd);
		RSA_set0_key(m_pRSAPrivate,&nbsp;bn,&nbsp;be,&nbsp;bd);
	}
	void&nbsp;SetPrivateKey(const&nbsp;std::string&amp;&nbsp;n,&nbsp;const&nbsp;std::string&amp;&nbsp;e,&nbsp;const&nbsp;std::string&amp;&nbsp;d)
	{
		SetPrivateKey(n.c_str(),&nbsp;(int)n.length(),&nbsp;e.c_str(),&nbsp;(int)e.length(),&nbsp;d.c_str(),&nbsp;(int)d.length());
	}

	
	//私钥加密
	std::string&nbsp;PrivateEncrypt(const&nbsp;void*&nbsp;data,&nbsp;int&nbsp;data_len)
	{
		std::string&nbsp;strRet;
		if&nbsp;(m_pRSAPrivate&nbsp;==&nbsp;NULL)
		{
			printf(&quot;private&nbsp;key&nbsp;is&nbsp;empyt.\n&quot;);
			return&nbsp;strRet;
		}

		int&nbsp;nRsaLen&nbsp;=&nbsp;0;
		int&nbsp;nBlockLen&nbsp;=&nbsp;0;
		int&nbsp;nBlockCount&nbsp;=&nbsp;0;
		int&nbsp;nPartResult&nbsp;=&nbsp;0;
		try
		{
			nRsaLen&nbsp;=&nbsp;RSA_size(m_pRSAPrivate);
			if&nbsp;(nRsaLen&nbsp;&lt;=&nbsp;0)
				throw&nbsp;ERR_get_error();
			nBlockLen&nbsp;=&nbsp;nRsaLen&nbsp;-&nbsp;m_nPaddingLen;

			nBlockCount&nbsp;=&nbsp;data_len&nbsp;/&nbsp;nBlockLen;
			if&nbsp;(data_len&nbsp;%&nbsp;nBlockLen)
				nBlockCount++;

			//分块加密
			std::unique_ptr&nbsp;&lt;unsigned&nbsp;char[]&gt;&nbsp;buff(new&nbsp;unsigned&nbsp;char[nRsaLen]);
			for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nBlockCount;&nbsp;i++)
			{
				int&nbsp;nDatalen&nbsp;=&nbsp;(std::min)(nBlockLen,&nbsp;data_len&nbsp;-&nbsp;(i&nbsp;*&nbsp;nBlockLen));
				nPartResult&nbsp;=&nbsp;RSA_private_encrypt(nDatalen,&nbsp;(unsigned&nbsp;char*)data&nbsp;+&nbsp;(i&nbsp;*&nbsp;nBlockLen),&nbsp;buff.get(),&nbsp;m_pRSAPrivate,&nbsp;m_nPaddingType);
				if&nbsp;(nPartResult&nbsp;&lt;&nbsp;0)
					throw&nbsp;ERR_get_error();

				strRet.append((char*)buff.get(),&nbsp;nPartResult);
			}
		}
		catch&nbsp;(unsigned&nbsp;long&nbsp;e)
		{
			printf(&quot;rsa&nbsp;private&nbsp;encrypt&nbsp;err&nbsp;%d.\n&quot;,&nbsp;e);
			strRet.clear();
		}

		return&nbsp;strRet;
	}
	std::string&nbsp;PrivateEncrypt(const&nbsp;std::string&amp;&nbsp;strData)
	{
		return&nbsp;PrivateEncrypt(strData.c_str(),&nbsp;(int)strData.length());
	}

	//公钥加密
	std::string&nbsp;PublicEncrypt(const&nbsp;void*&nbsp;data,&nbsp;int&nbsp;data_len)
	{
		std::string&nbsp;strRet;
		if&nbsp;(m_pRSAPublic&nbsp;==&nbsp;NULL)
		{
			printf(&quot;public&nbsp;key&nbsp;is&nbsp;empyt.\n&quot;);
			return&nbsp;strRet;
		}

		int&nbsp;nRsaLen&nbsp;=&nbsp;0;
		int&nbsp;nBlockLen&nbsp;=&nbsp;0;
		int&nbsp;nBlockCount&nbsp;=&nbsp;0;
		int&nbsp;nPartResult&nbsp;=&nbsp;0;
		try
		{
			nRsaLen&nbsp;=&nbsp;RSA_size(m_pRSAPublic);
			if&nbsp;(nRsaLen&nbsp;&lt;=&nbsp;0)
				throw&nbsp;ERR_get_error();
			nBlockLen&nbsp;=&nbsp;nRsaLen&nbsp;-&nbsp;m_nPaddingLen;

			nBlockCount&nbsp;=&nbsp;data_len&nbsp;/&nbsp;nBlockLen;
			if&nbsp;(data_len&nbsp;%&nbsp;nBlockLen)
				nBlockCount++;

			//分块加密
			std::unique_ptr&nbsp;&lt;unsigned&nbsp;char[]&gt;&nbsp;buff(new&nbsp;unsigned&nbsp;char[nRsaLen]);
			for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nBlockCount;&nbsp;i++)
			{
				int&nbsp;nDatalen&nbsp;=&nbsp;(std::min)(nBlockLen,&nbsp;data_len&nbsp;-&nbsp;(i&nbsp;*&nbsp;nBlockLen));
				nPartResult&nbsp;=&nbsp;RSA_public_encrypt(nDatalen,&nbsp;(unsigned&nbsp;char*)data&nbsp;+&nbsp;(i&nbsp;*&nbsp;nBlockLen),&nbsp;buff.get(),&nbsp;m_pRSAPublic,&nbsp;m_nPaddingType);
				if&nbsp;(nPartResult&nbsp;&lt;&nbsp;0)
					throw&nbsp;ERR_get_error();

				strRet.append((char*)buff.get(),&nbsp;nPartResult);
			}
		}
		catch&nbsp;(unsigned&nbsp;long&nbsp;e)
		{
			printf(&quot;rsa&nbsp;public&nbsp;encrypt&nbsp;err&nbsp;%d.\n&quot;,&nbsp;e);
			strRet.clear();
		}

		return&nbsp;strRet;
	}
	std::string&nbsp;PublicEncrypt(const&nbsp;std::string&amp;&nbsp;strData)
	{
		return&nbsp;PublicEncrypt(strData.c_str(),&nbsp;(int)strData.length());
	}


	//私钥解密
	std::string&nbsp;PrivateDecrypt(const&nbsp;void*&nbsp;data,&nbsp;int&nbsp;data_len)
	{
		std::string&nbsp;strRet;
		if&nbsp;(m_pRSAPrivate&nbsp;==&nbsp;NULL)
		{
			printf(&quot;private&nbsp;key&nbsp;is&nbsp;empyt.\n&quot;);
			return&nbsp;strRet;
		}

		int&nbsp;nRsaLen&nbsp;=&nbsp;0;
		int&nbsp;nBlockCount&nbsp;=&nbsp;0;
		int&nbsp;nPartResult&nbsp;=&nbsp;0;
		try
		{
			nRsaLen&nbsp;=&nbsp;RSA_size(m_pRSAPrivate);
			if&nbsp;(nRsaLen&nbsp;&lt;=&nbsp;0)
				throw&nbsp;ERR_get_error();

			if&nbsp;(data_len&nbsp;%&nbsp;nRsaLen)
				throw&nbsp;0;
			nBlockCount&nbsp;=&nbsp;data_len&nbsp;/&nbsp;nRsaLen;


			//分块解密
			std::unique_ptr&nbsp;&lt;unsigned&nbsp;char[]&gt;&nbsp;buff(new&nbsp;unsigned&nbsp;char[nRsaLen]);
			for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nBlockCount;&nbsp;i++)
			{
				//
				nPartResult&nbsp;=&nbsp;RSA_private_decrypt(nRsaLen,&nbsp;(unsigned&nbsp;char*)data&nbsp;+&nbsp;(i&nbsp;*&nbsp;nRsaLen),&nbsp;buff.get(),&nbsp;m_pRSAPrivate,&nbsp;m_nPaddingType);
				if&nbsp;(nPartResult&nbsp;&lt;&nbsp;0)
					throw&nbsp;ERR_get_error();

				strRet.append((char*)buff.get(),&nbsp;nPartResult);
			}
		}
		catch&nbsp;(unsigned&nbsp;long&nbsp;e)
		{
			char&nbsp;errbuf[256];
			printf(&quot;rsa&nbsp;private&nbsp;decrypt&nbsp;err&nbsp;%s(%d).\n&quot;,&nbsp;ERR_error_string(e,&nbsp;errbuf),&nbsp;e);
			strRet.clear();
		}

		return&nbsp;strRet;
	}
	std::string&nbsp;PrivateDecrypt(const&nbsp;std::string&amp;&nbsp;strData)
	{
		return&nbsp;PrivateDecrypt(strData.c_str(),&nbsp;(int)strData.length());
	}

	//公钥解密
	std::string&nbsp;PublicDecrypt(const&nbsp;void*&nbsp;data,&nbsp;int&nbsp;data_len)
	{
		std::string&nbsp;strRet;
		if&nbsp;(m_pRSAPublic&nbsp;==&nbsp;NULL)
		{
			printf(&quot;public&nbsp;key&nbsp;is&nbsp;empyt.\n&quot;);
			return&nbsp;strRet;
		}

		int&nbsp;nRsaLen&nbsp;=&nbsp;0;
		int&nbsp;nBlockCount&nbsp;=&nbsp;0;
		int&nbsp;nPartResult&nbsp;=&nbsp;0;
		try
		{
			nRsaLen&nbsp;=&nbsp;RSA_size(m_pRSAPublic);
			if&nbsp;(nRsaLen&nbsp;&lt;=&nbsp;0)
				throw&nbsp;ERR_get_error();

			if&nbsp;(data_len&nbsp;%&nbsp;nRsaLen)
				throw&nbsp;0;
			nBlockCount&nbsp;=&nbsp;data_len&nbsp;/&nbsp;nRsaLen;


			//分块解密
			std::unique_ptr&nbsp;&lt;unsigned&nbsp;char[]&gt;&nbsp;buff(new&nbsp;unsigned&nbsp;char[nRsaLen]);
			for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nBlockCount;&nbsp;i++)
			{
				//
				nPartResult&nbsp;=&nbsp;RSA_public_decrypt(nRsaLen,&nbsp;(unsigned&nbsp;char*)data&nbsp;+&nbsp;(i&nbsp;*&nbsp;nRsaLen),&nbsp;buff.get(),&nbsp;m_pRSAPublic,&nbsp;m_nPaddingType);
				if&nbsp;(nPartResult&nbsp;&lt;&nbsp;0)
					throw&nbsp;ERR_get_error();

				strRet.append((char*)buff.get(),&nbsp;nPartResult);
			}
		}
		catch&nbsp;(unsigned&nbsp;long&nbsp;e)
		{
			printf(&quot;rsa&nbsp;public&nbsp;decrypt&nbsp;err&nbsp;%d.\n&quot;,&nbsp;e);
			strRet.clear();
		}

		return&nbsp;strRet;
	}
	std::string&nbsp;PublicDecrypt(const&nbsp;std::string&amp;&nbsp;strData)
	{
		return&nbsp;PublicDecrypt(strData.c_str(),&nbsp;(int)strData.length());
	}

protected:
	RSA&nbsp;*m_pRSAPrivate;
	RSA&nbsp;*m_pRSAPublic;
	int&nbsp;m_nPaddingType;
	int&nbsp;m_nPaddingLen;

	BIO*&nbsp;_GetBIOFromKeyString(const&nbsp;std::string&nbsp;strKey)
	{
		BIO*&nbsp;pKEY&nbsp;=&nbsp;NULL;
		try
		{
			pKEY&nbsp;=&nbsp;BIO_new(BIO_s_mem());
			if&nbsp;(pKEY&nbsp;==&nbsp;NULL)
				throw&nbsp;ERR_get_error();

			int&nbsp;nResult&nbsp;=&nbsp;BIO_write(pKEY,&nbsp;strKey.c_str(),&nbsp;(int)strKey.length());
			if&nbsp;(nResult&nbsp;&lt;=&nbsp;0)
				throw&nbsp;ERR_get_error();
		}
		catch&nbsp;(unsigned&nbsp;long&nbsp;e)
		{
			printf(&quot;rsa&nbsp;bio&nbsp;key&nbsp;err&nbsp;%d.\n&quot;,&nbsp;e);
			if&nbsp;(pKEY)
				BIO_free_all(pKEY);

			return&nbsp;NULL;
		}

		return&nbsp;pKEY;
	}
};</pre><p><br/></p><p><br/></p><p>！</p>]]></description><category>C/C++代码</category><comments>http://www.fenlog.com/post/131.html#comment</comments><wfw:commentRss>http://www.fenlog.com/feed.asp?cmt=131</wfw:commentRss></item><item><title>plist转json解析类plist2json(libplist C++封装类)</title><author>345382462@qq.com (blackfeather)</author><link>http://www.fenlog.com/post/130.html</link><pubDate>Wed, 27 Dec 2023 16:26:39 +0800</pubDate><guid>http://www.fenlog.com/post/130.html</guid><description><![CDATA[<p><br/></p><p>苹果配置中大量用到了plist，使用开源的C语言的库libplist可以读取解析，但是纯C的写起来非常蛋疼。</p><p><br/></p><p>于是用C++封装了一下(<span style="text-wrap: wrap;">c++ wrapper</span>)，但是plist的本质还是xml，读取起来还是略有繁琐，于是转为json结构，就可以直接使用了（依赖jsoncpp库）。</p><p><br/></p><p>plist内部是有PLIST_UID、PLIST_DICT、PLIST_ARRAY等复杂的结构，支持xml和binary(bplist)两种格式，支持uid自动解析处理关联，支持NS.objects、NS.keys、NSDictionary、NSArray、NSMutableDictionary、NSMutableArray等结构自动处理，使用起来就非常方便了。</p><p><br/></p><p>header only引用：<span style="text-wrap: wrap;">plistObject.h</span></p><pre class="brush:cpp;toolbar:false">#pragma&nbsp;once

#include&nbsp;&lt;json/json.h&gt;
#include&nbsp;&lt;plist/plist.h&gt;

#include&nbsp;&quot;base64.h&quot;

//屏蔽掉调试输出
//#define&nbsp;_bplist_printf(a,...)&nbsp;printf(a,&nbsp;##__VA_ARGS__)
#define&nbsp;_bplist_printf(a,...)&nbsp;

//#define&nbsp;plist_to_bin_free(p)&nbsp;plist_mem_free(p)
//#define&nbsp;plist_to_xml_free(p)&nbsp;plist_mem_free(p)

class&nbsp;CPlistObject&nbsp;:&nbsp;public&nbsp;Json::Value
{
public:
	template&nbsp;&lt;class&nbsp;T&gt;
	static&nbsp;Json::Value&nbsp;Plist2Json(const&nbsp;T&amp;&nbsp;filepath,&nbsp;bool&nbsp;bConvertDataPlist&nbsp;=&nbsp;false)
	{
		CPlistObject&nbsp;plist(bConvertDataPlist);
		if&nbsp;(plist.OpenPlistFile(filepath))
			return&nbsp;std::move(plist);

		return&nbsp;{};
	}
	static&nbsp;Json::Value&nbsp;PlistBuf2Json(const&nbsp;std::string&amp;&nbsp;buf,&nbsp;bool&nbsp;bConvertDataPlist&nbsp;=&nbsp;false)
	{
		CPlistObject&nbsp;plist(bConvertDataPlist);
		if&nbsp;(plist.OpenPlistBuf(buf))
			return&nbsp;std::move(plist);

		return&nbsp;{};
	}

	CPlistObject(bool&nbsp;bConvertDataPlist&nbsp;=&nbsp;false)
	{
		m_plistMain&nbsp;=&nbsp;nullptr;
		m_pObjectArray&nbsp;=&nbsp;nullptr;
		m_bConvertDataPlist&nbsp;=&nbsp;bConvertDataPlist;
	}

	~CPlistObject()
	{
		Close();
	}

	void&nbsp;Close()
	{
		if&nbsp;(m_plistMain)
		{
			plist_free(m_plistMain);
			m_plistMain&nbsp;=&nbsp;nullptr;
		}

		this-&gt;clear();
	}

	//打开plist内存缓冲区
	bool&nbsp;OpenPlistBuf(const&nbsp;void*&nbsp;lpBuffer,&nbsp;size_t&nbsp;nBufLen)
	{
		Close();

		if&nbsp;(memcmp(lpBuffer,&nbsp;&quot;bplist00&quot;,&nbsp;8)&nbsp;==&nbsp;0)
			plist_from_bin((const&nbsp;char*)lpBuffer,&nbsp;(uint32_t)nBufLen,&nbsp;&amp;m_plistMain);
		else&nbsp;//if&nbsp;(memcmp(lpBuffer,&nbsp;&quot;&lt;?xml&quot;,&nbsp;5)&nbsp;==&nbsp;0)
			plist_from_xml((const&nbsp;char*)lpBuffer,&nbsp;(uint32_t)nBufLen,&nbsp;&amp;m_plistMain);

		if&nbsp;(!m_plistMain)
			return&nbsp;false;

		if&nbsp;(!_ConvertPlistObject(*this))
			return&nbsp;false;

		return&nbsp;true;
	}
	bool&nbsp;OpenPlistBuf(const&nbsp;std::string&amp;&nbsp;buf)
	{
		return&nbsp;OpenPlistBuf(buf.c_str(),&nbsp;buf.length());
	}
	//打开plist文件
	template&nbsp;&lt;class&nbsp;T&gt;
	bool&nbsp;OpenPlistFile(const&nbsp;T&amp;&nbsp;filepath)
	{
		Close();

		std::ifstream&nbsp;ifs(filepath,&nbsp;std::ios::binary);
		if&nbsp;(!ifs.is_open())
			return&nbsp;false;

		unsigned&nbsp;char&nbsp;header[4]&nbsp;=&nbsp;{&nbsp;0&nbsp;};
		ifs.read((char*)header,&nbsp;3);
		//兼容win&nbsp;utf8&nbsp;bom&nbsp;0xEFBBBF
		if&nbsp;(header[0]&nbsp;==&nbsp;0xEF&nbsp;&amp;&amp;&nbsp;header[1]&nbsp;==&nbsp;0xBB&nbsp;&amp;&amp;&nbsp;header[2]&nbsp;==&nbsp;0xBF)
		{
			//貌似不需要处理啥
		}
		else
		{
			//指针回到开头
			ifs.seekg(0,&nbsp;std::ios::beg);
		}

		//读取文件内容
		std::ostringstream&nbsp;sstr;
		sstr&nbsp;&lt;&lt;&nbsp;ifs.rdbuf();
		std::string&nbsp;buf&nbsp;=&nbsp;sstr.str();

		//读文件失败了
		if&nbsp;(buf.empty())
			return&nbsp;false;

		return&nbsp;OpenPlistBuf(buf.c_str(),&nbsp;buf.length());
	}

	//转换plist为json
//&nbsp;	bool&nbsp;ConvertPlistObject(bool&nbsp;bConvertDataPlist&nbsp;=&nbsp;false)&nbsp;
//&nbsp;	{
//&nbsp;		if&nbsp;(bConvertDataPlist)
//&nbsp;		return&nbsp;_ConvertPlistObject(m_jsonValues);
//&nbsp;	}


private:

	bool&nbsp;_ConvertPlistObject(Json::Value&amp;&nbsp;jsonObject)
	{
		m_pObjectArray&nbsp;=&nbsp;plist_dict_get_item(m_plistMain,&nbsp;&quot;$objects&quot;);
		plist_t&nbsp;pTop&nbsp;=&nbsp;plist_dict_get_item(m_plistMain,&nbsp;&quot;$top&quot;);
		if&nbsp;(m_pObjectArray&nbsp;&amp;&amp;&nbsp;plist_get_node_type(m_pObjectArray)&nbsp;==&nbsp;PLIST_ARRAY&nbsp;||
			pTop&nbsp;&amp;&amp;&nbsp;plist_get_node_type(pTop)&nbsp;==&nbsp;PLIST_DICT)
		{
			//modify&nbsp;by&nbsp;fenlog&nbsp;2026.02.23&nbsp;直接将$top指向的数据作为json的根节点了，如果数据是array那么这么附加会出问题
			//这两个数据不重要，直接无视了
//&nbsp;			plist_t&nbsp;pVersionNode&nbsp;=&nbsp;plist_dict_get_item(m_plistMain,&nbsp;&quot;$version&quot;);
//&nbsp;			if&nbsp;(pVersionNode)
//&nbsp;				SetValue(pVersionNode,&nbsp;jsonObject[&quot;$version&quot;]);
//&nbsp;			plist_t&nbsp;pArchiverNode&nbsp;=&nbsp;plist_dict_get_item(m_plistMain,&nbsp;&quot;$archiver&quot;);
//&nbsp;			if&nbsp;(pArchiverNode)
//&nbsp;				SetValue(pArchiverNode,&nbsp;jsonObject[&quot;$archiver&quot;]);

			//获取root节点
			plist_t&nbsp;pRoot&nbsp;=&nbsp;plist_dict_get_item(pTop,&nbsp;&quot;root&quot;);
			if&nbsp;(pRoot)
			{
				int&nbsp;nRootType&nbsp;=&nbsp;plist_get_node_type(pRoot);
				//几乎所有的plist文件都是通过uid引用的
				if&nbsp;(nRootType&nbsp;==&nbsp;PLIST_UID)
				{
					plist_t&nbsp;pRootObj&nbsp;=&nbsp;GetNodeByUidDict(pRoot);
					if&nbsp;(pRootObj)
					{
						ConvertDict(pRootObj,&nbsp;jsonObject);
						return&nbsp;(!jsonObject.isNull());
					}
				}
				else&nbsp;if&nbsp;(nRootType&nbsp;==&nbsp;PLIST_DICT)
				{
					ConvertDict(pRoot,&nbsp;jsonObject);
					return&nbsp;(!jsonObject.isNull());
				}
			}
			else
			{
				//有些没有root节点&nbsp;直接是字典
				ConvertDict(pTop,&nbsp;jsonObject);

				return&nbsp;(!jsonObject.isNull());
			}
		}

		int&nbsp;nType&nbsp;=&nbsp;plist_get_node_type(m_plistMain);
		if&nbsp;(nType&nbsp;==&nbsp;PLIST_DICT)
		{
			EnumDictKeyValueDefault(m_plistMain,&nbsp;jsonObject);
		}
		else&nbsp;if&nbsp;(nType&nbsp;==&nbsp;PLIST_ARRAY)
		{
			EnumArray(m_plistMain,&nbsp;jsonObject);
		}
		else
		{
			SetValue(m_plistMain,&nbsp;jsonObject);
		}

		return&nbsp;(!jsonObject.isNull());
	}

	plist_t&nbsp;GetNodeByUidDict(plist_t&nbsp;pDict)&nbsp;const
	{
		if&nbsp;(!pDict)
			return&nbsp;nullptr;

		plist_type&nbsp;nType&nbsp;=&nbsp;plist_get_node_type(pDict);
		//如果类型不是UID的&nbsp;直接就返回原元素指针
		if&nbsp;(nType&nbsp;!=&nbsp;PLIST_UID)
			return&nbsp;pDict;

		plist_t&nbsp;pRet&nbsp;=&nbsp;nullptr;

		uint64_t&nbsp;val&nbsp;=&nbsp;0;
		plist_get_uid_val(pDict,&nbsp;&amp;val);
		pRet&nbsp;=&nbsp;plist_array_get_item(m_pObjectArray,&nbsp;(uint32_t)val);

		return&nbsp;pRet;
	}

	std::string&nbsp;GetDictClassName(plist_t&nbsp;pDict)
	{
		std::string&nbsp;strRet;
		plist_t&nbsp;pClassName&nbsp;=&nbsp;GetNodeByUidDict(pDict);
		if&nbsp;(pClassName)
		{
			if&nbsp;(plist_get_node_type(pClassName)&nbsp;==&nbsp;PLIST_DICT)
			{
				plist_t&nbsp;pName&nbsp;=&nbsp;plist_dict_get_item(pClassName,&nbsp;&quot;$classname&quot;);
				if&nbsp;(pName)
				{
					uint64_t&nbsp;sLen&nbsp;=&nbsp;0;
					const&nbsp;char*&nbsp;pszName&nbsp;=&nbsp;plist_get_string_ptr(pName,&nbsp;&amp;sLen);
					if&nbsp;(pszName)
						strRet&nbsp;=&nbsp;pszName;
				}
			}
		}

		return&nbsp;strRet;
	}

	void&nbsp;SetValue(plist_t&nbsp;pValue,&nbsp;Json::Value&amp;&nbsp;jsonValue)&nbsp;const
	{
		int&nbsp;nType&nbsp;=&nbsp;plist_get_node_type(pValue);
		if&nbsp;(nType&nbsp;==&nbsp;PLIST_STRING)
		{
			uint64_t&nbsp;sLen&nbsp;=&nbsp;0;
			const&nbsp;char*&nbsp;pszValue&nbsp;=&nbsp;plist_get_string_ptr(pValue,&nbsp;&amp;sLen);
			if&nbsp;(pszValue)
			{
				_bplist_printf(&quot;val(str):%s&nbsp;\n&quot;,&nbsp;pszValue);
				jsonValue&nbsp;=&nbsp;pszValue;
			}
		}
		else&nbsp;if&nbsp;(nType&nbsp;==&nbsp;PLIST_UINT)
		{
			uint64_t&nbsp;nValue&nbsp;=&nbsp;0;
			plist_get_uint_val(pValue,&nbsp;(uint64_t*)&amp;nValue);
			_bplist_printf(&quot;val(uint):%llu&nbsp;\n&quot;,&nbsp;nValue);
			jsonValue&nbsp;=&nbsp;nValue;
		}
		else&nbsp;if&nbsp;(nType&nbsp;==&nbsp;PLIST_BOOLEAN)
		{
			uint8_t&nbsp;nValue&nbsp;=&nbsp;0;
			plist_get_bool_val(pValue,&nbsp;&amp;nValue);
			_bplist_printf(&quot;val(bool):%d&nbsp;\n&quot;,&nbsp;nValue);
			jsonValue&nbsp;=&nbsp;nValue;
		}
		else&nbsp;if&nbsp;(nType&nbsp;==&nbsp;PLIST_DATA)
		{
			uint64_t&nbsp;nDataLen&nbsp;=&nbsp;0;
			const&nbsp;char*&nbsp;pszValue&nbsp;=&nbsp;plist_get_data_ptr(pValue,&nbsp;&amp;nDataLen);

			std::string&nbsp;strValue;
			if&nbsp;(nDataLen&nbsp;&gt;&nbsp;0)
			{
				//add&nbsp;by&nbsp;fenlog&nbsp;2019.11.07&nbsp;转义data中的plist
				if&nbsp;(m_bConvertDataPlist&nbsp;&amp;&amp;&nbsp;nDataLen&nbsp;&gt;&nbsp;16&nbsp;&amp;&amp;&nbsp;(memcmp(pszValue,&nbsp;&quot;bplist00&quot;,&nbsp;8)&nbsp;==&nbsp;0&nbsp;||&nbsp;memcmp(pszValue,&nbsp;&quot;&lt;?xml&quot;,&nbsp;5)&nbsp;==&nbsp;0))
				{
					CPlistObject&nbsp;mSubPlist(true);
					if&nbsp;(mSubPlist.OpenPlistBuf(pszValue,&nbsp;(size_t)nDataLen))
					{
						jsonValue&nbsp;=&nbsp;mSubPlist;
						return;
					}
				}

				strValue&nbsp;=&nbsp;base64_encode((const&nbsp;unsigned&nbsp;char*)pszValue,&nbsp;(size_t)nDataLen);
			}

			_bplist_printf(&quot;val(data):%s&nbsp;\n&quot;,&nbsp;strValue.c_str());
			jsonValue&nbsp;=&nbsp;strValue;
		}
		else&nbsp;if&nbsp;(nType&nbsp;==&nbsp;PLIST_REAL)
		{
			double&nbsp;nValue;
			plist_get_real_val(pValue,&nbsp;&amp;nValue);
			_bplist_printf(&quot;val(real):%lf&nbsp;\n&quot;,&nbsp;nValue);
			jsonValue&nbsp;=&nbsp;nValue;
		}
		else&nbsp;if&nbsp;(nType&nbsp;==&nbsp;PLIST_DATE)
		{
			int32_t&nbsp;nSec,&nbsp;nMSec;
			plist_get_date_val(pValue,&nbsp;&amp;nSec,&nbsp;&amp;nMSec);
			uint32_t&nbsp;dwSec&nbsp;=&nbsp;nSec&nbsp;+&nbsp;978278400;&nbsp;//Represents&nbsp;the&nbsp;number&nbsp;of&nbsp;seconds&nbsp;since&nbsp;01/01/2001
			jsonValue&nbsp;=&nbsp;dwSec;
		}
		else
		{
			_bplist_printf(&quot;val(%d)&nbsp;\n&quot;,&nbsp;nType);
		}
	}

	inline&nbsp;bool&nbsp;CheckItemType(plist_t&nbsp;pNode,&nbsp;plist_type&nbsp;nType)
	{
		if&nbsp;(pNode&nbsp;&amp;&amp;&nbsp;plist_get_node_type(pNode)&nbsp;==&nbsp;nType)
			return&nbsp;true;

		return&nbsp;false;
	}

	void&nbsp;EnumDictNSMutableArray(plist_t&nbsp;pDictObject,&nbsp;Json::Value&amp;&nbsp;jsonValue)
	{
		plist_t&nbsp;pNSObjects&nbsp;=&nbsp;plist_dict_get_item(pDictObject,&nbsp;&quot;NS.objects&quot;);
		if&nbsp;(CheckItemType(pNSObjects,&nbsp;PLIST_ARRAY))
		{
			uint32_t&nbsp;nObjects&nbsp;=&nbsp;plist_array_get_size(pNSObjects);
			for&nbsp;(uint32_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nObjects;&nbsp;i++)
			{
				plist_t&nbsp;pValue&nbsp;=&nbsp;GetNodeByUidDict(plist_array_get_item(pNSObjects,&nbsp;i));
				if&nbsp;(pValue)
				{
					plist_type&nbsp;nType&nbsp;=&nbsp;plist_get_node_type(pValue);
					_bplist_printf(&quot;%d&nbsp;-&nbsp;type&nbsp;%d&nbsp;&quot;,&nbsp;i,&nbsp;nType);

					if&nbsp;(nType&nbsp;==&nbsp;PLIST_DICT)
					{
						_bplist_printf(&quot;\n&quot;);
						ConvertDict(pValue,&nbsp;jsonValue[i]);
					}
					else
					{
						SetValue(pValue,&nbsp;jsonValue[i]);
					}
				}
			}
		}
	}

	void&nbsp;EnumDictNSMutableDictionary(plist_t&nbsp;pDictObject,&nbsp;Json::Value&amp;&nbsp;jsonValue)
	{
		plist_t&nbsp;pNSKeys&nbsp;=&nbsp;plist_dict_get_item(pDictObject,&nbsp;&quot;NS.keys&quot;);
		plist_t&nbsp;pNSObjects&nbsp;=&nbsp;plist_dict_get_item(pDictObject,&nbsp;&quot;NS.objects&quot;);
		if&nbsp;(CheckItemType(pNSKeys,&nbsp;PLIST_ARRAY)&nbsp;&amp;&amp;&nbsp;CheckItemType(pNSObjects,&nbsp;PLIST_ARRAY))
		{
			uint32_t&nbsp;nKeys&nbsp;=&nbsp;plist_array_get_size(pNSKeys);
			if&nbsp;(plist_array_get_size(pNSObjects)&nbsp;==&nbsp;nKeys)
			{
				for&nbsp;(uint32_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nKeys;&nbsp;i++)
				{
					plist_t&nbsp;pKey&nbsp;=&nbsp;GetNodeByUidDict(plist_array_get_item(pNSKeys,&nbsp;i));
					plist_t&nbsp;pValue&nbsp;=&nbsp;GetNodeByUidDict(plist_array_get_item(pNSObjects,&nbsp;i));
					if&nbsp;(pKey&nbsp;&amp;&amp;&nbsp;pValue)
					{
						//modify&nbsp;by&nbsp;fenlog&nbsp;2019.07.18
						plist_type&nbsp;nKeyType&nbsp;=&nbsp;plist_get_node_type(pKey);
						plist_type&nbsp;nValueType&nbsp;=&nbsp;plist_get_node_type(pValue);
						if&nbsp;(nKeyType&nbsp;==&nbsp;PLIST_STRING)
						{
							uint64_t&nbsp;sLen&nbsp;=&nbsp;0;
							const&nbsp;char*&nbsp;pszKey&nbsp;=&nbsp;plist_get_string_ptr(pKey,&nbsp;&amp;sLen);
							if&nbsp;(pszKey)
							{
								_bplist_printf(&quot;%s&nbsp;-&nbsp;type&nbsp;%d&nbsp;&quot;,&nbsp;pszKey,&nbsp;nValueType);

								if&nbsp;(nValueType&nbsp;==&nbsp;PLIST_DICT)
								{
									_bplist_printf(&quot;\n&quot;);
									ConvertDict(pValue,&nbsp;jsonValue[pszKey]);
								}
								else
								{
									SetValue(pValue,&nbsp;jsonValue[pszKey]);
								}
							}
						}
						else&nbsp;if&nbsp;(nKeyType&nbsp;==&nbsp;PLIST_UINT)
						{
							uint64_t&nbsp;nKeyValue&nbsp;=&nbsp;0;
							plist_get_uint_val(pKey,&nbsp;&amp;nKeyValue);

							_bplist_printf(&quot;%llu&nbsp;-&nbsp;type&nbsp;%d&nbsp;&quot;,&nbsp;nKeyValue,&nbsp;nValueType);

							if&nbsp;(nValueType&nbsp;==&nbsp;PLIST_DICT)
							{
								_bplist_printf(&quot;\n&quot;);
								ConvertDict(pValue,&nbsp;jsonValue[std::to_string(nKeyValue)]);
							}
							else
							{
								SetValue(pValue,&nbsp;jsonValue[std::to_string(nKeyValue)]);
							}
						}
						else&nbsp;if&nbsp;(nKeyType&nbsp;==&nbsp;PLIST_DICT)
						{
							std::string&nbsp;index&nbsp;=&nbsp;std::to_string(i);
							ConvertDict(pKey,&nbsp;jsonValue[index][&quot;key&quot;]);
							if&nbsp;(nValueType&nbsp;==&nbsp;PLIST_DICT)
								ConvertDict(pValue,&nbsp;jsonValue[index][&quot;value&quot;]);
							else
								SetValue(pValue,&nbsp;jsonValue[index][&quot;value&quot;]);
						}
						else
						{
							_bplist_printf(&quot;unknow&nbsp;type&nbsp;%d&nbsp;\n&quot;,&nbsp;nKeyType);
						}
					}
				}
			}
		}
	}

	void&nbsp;EnumArray(plist_t&nbsp;pArrObject,&nbsp;Json::Value&amp;&nbsp;jsonValue)
	{
		plist_type&nbsp;nType&nbsp;=&nbsp;plist_get_node_type(pArrObject);
		if&nbsp;(nType&nbsp;!=&nbsp;PLIST_ARRAY)
			return;

		uint32_t&nbsp;nArrayCount&nbsp;=&nbsp;plist_array_get_size(pArrObject);
		for&nbsp;(uint32_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nArrayCount;&nbsp;i++)
		{
			plist_t&nbsp;pItem&nbsp;=&nbsp;plist_array_get_item(pArrObject,&nbsp;i);
			nType&nbsp;=&nbsp;plist_get_node_type(pItem);
			if&nbsp;(nType&nbsp;==&nbsp;PLIST_DICT)
			{
				EnumDictKeyValueDefault(pItem,&nbsp;jsonValue[i]);
			}
			else&nbsp;if&nbsp;(nType&nbsp;==&nbsp;PLIST_ARRAY)
			{
				EnumArray(pItem,&nbsp;jsonValue[i]);
			}
			else
			{
				SetValue(pItem,&nbsp;jsonValue[i]);
			}
		}
	}

	void&nbsp;EnumDictKeyValueDefault(plist_t&nbsp;pDictObject,&nbsp;Json::Value&amp;&nbsp;jsonValue)
	{
		plist_dict_iter&nbsp;it&nbsp;=&nbsp;nullptr;
		char*&nbsp;key&nbsp;=&nbsp;nullptr;
		plist_t&nbsp;subnode&nbsp;=&nbsp;nullptr;
		plist_dict_new_iter(pDictObject,&nbsp;&amp;it);
		plist_dict_next_item(pDictObject,&nbsp;it,&nbsp;&amp;key,&nbsp;&amp;subnode);
		while&nbsp;(subnode)
		{
			int&nbsp;nType&nbsp;=&nbsp;plist_get_node_type(subnode);
			//UID的重新修正目标指针
			if&nbsp;(nType&nbsp;==&nbsp;PLIST_UID)
			{
				subnode&nbsp;=&nbsp;GetNodeByUidDict(subnode);
				//重新读取type
				if&nbsp;(subnode)
					nType&nbsp;=&nbsp;plist_get_node_type(subnode);
			}

			_bplist_printf(&quot;%s&nbsp;-&nbsp;type&nbsp;%d&nbsp;\n&quot;,&nbsp;key,&nbsp;nType);

			if&nbsp;(nType&nbsp;==&nbsp;PLIST_DICT)
			{
				_bplist_printf(&quot;\n&quot;);
				ConvertDict(subnode,&nbsp;jsonValue[key]);
			}
			else&nbsp;if&nbsp;(nType&nbsp;==&nbsp;PLIST_ARRAY)
			{
				EnumArray(subnode,&nbsp;jsonValue[key]);
			}
			else
			{
				SetValue(subnode,&nbsp;jsonValue[key]);
			}

			subnode&nbsp;=&nbsp;nullptr;
			plist_to_bin_free(key);&nbsp;//没有plist_mem_free，临时用这个代替
			key&nbsp;=&nbsp;nullptr;
			plist_dict_next_item(pDictObject,&nbsp;it,&nbsp;&amp;key,&nbsp;&amp;subnode);
		}
		plist_to_bin_free((char*)it);//没有plist_mem_free，临时用这个代替
	}

	void&nbsp;ConvertDict(plist_t&nbsp;pDict,&nbsp;Json::Value&amp;&nbsp;jsonValue)
	{
		//如果是dict&nbsp;可能是对象描述
		plist_t&nbsp;pDictObject&nbsp;=&nbsp;plist_dict_get_item(pDict,&nbsp;&quot;$class&quot;);
		if&nbsp;(pDictObject)
		{
			std::string&nbsp;strClassName&nbsp;=&nbsp;GetDictClassName(pDictObject);

			//一般这里都不会为空
			if&nbsp;(!strClassName.empty())
			{
				_bplist_printf(&quot;Get&nbsp;Class:%s&nbsp;\n&quot;,&nbsp;strClassName.c_str());

				if&nbsp;(strClassName&nbsp;==&nbsp;&quot;NSMutableDictionary&quot;&nbsp;||&nbsp;strClassName&nbsp;==&nbsp;&quot;NSDictionary&quot;)
				{
					EnumDictNSMutableDictionary(pDict,&nbsp;jsonValue);
				}
				else&nbsp;if&nbsp;(strClassName&nbsp;==&nbsp;&quot;NSMutableArray&quot;&nbsp;||&nbsp;strClassName&nbsp;==&nbsp;&quot;NSArray&quot;)
				{
					EnumDictNSMutableArray(pDict,&nbsp;jsonValue);
				}
				else
				{
					//遍历里面的key-value
					EnumDictKeyValueDefault(pDict,&nbsp;jsonValue);
				}
			}
		}
		else
		{
			EnumDictKeyValueDefault(pDict,&nbsp;jsonValue);
		}
	}

	std::string&nbsp;ConvertPlistToXML(plist_t&nbsp;plist&nbsp;=&nbsp;nullptr)&nbsp;const
	{
		std::string&nbsp;strRet;
		if&nbsp;(!plist)
		{
			if&nbsp;(!m_plistMain)
				return&nbsp;strRet;

			plist&nbsp;=&nbsp;m_plistMain;
		}

		char*&nbsp;pszMsg&nbsp;=&nbsp;nullptr;
		uint32_t&nbsp;nMsgLen&nbsp;=&nbsp;0;
		plist_to_xml(plist,&nbsp;&amp;pszMsg,&nbsp;&amp;nMsgLen);
		//printf(&quot;%s&nbsp;\n&quot;,&nbsp;pszMsg);
		if&nbsp;(pszMsg)
		{
			strRet&nbsp;=&nbsp;pszMsg;
			plist_to_xml_free(pszMsg);
		}

		return&nbsp;strRet;
	}

	plist_t&nbsp;m_plistMain;
	plist_t&nbsp;m_pObjectArray;
	bool&nbsp;m_bConvertDataPlist;
};</pre><p><br/></p><p><br/></p><p><br/></p><p><br/></p><p>比如一个示例的plist：demo.plist</p><pre class="brush:xml;toolbar:false">&lt;?xml&nbsp;version=&quot;1.0&quot;&nbsp;encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE&nbsp;plist&nbsp;PUBLIC&nbsp;&quot;-//Apple//DTD&nbsp;PLIST&nbsp;1.0//EN&quot;&nbsp;&quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
&lt;plist&nbsp;version=&quot;1.0&quot;&gt;
&lt;dict&gt;
	&lt;key&gt;$version&lt;/key&gt;
	&lt;integer&gt;100000&lt;/integer&gt;
	&lt;key&gt;$archiver&lt;/key&gt;
	&lt;string&gt;NSKeyedArchiver&lt;/string&gt;
	&lt;key&gt;$top&lt;/key&gt;
	&lt;dict&gt;
		&lt;key&gt;root&lt;/key&gt;
		&lt;dict&gt;
			&lt;key&gt;CF$UID&lt;/key&gt;
			&lt;integer&gt;1&lt;/integer&gt;
		&lt;/dict&gt;
	&lt;/dict&gt;
	&lt;key&gt;$objects&lt;/key&gt;
	&lt;array&gt;
		&lt;string&gt;$null&lt;/string&gt;
		&lt;dict&gt;
			&lt;key&gt;_closeLevel&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_intimateRelationDark&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_sex&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;9&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_gameLastLoginTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_closeDays&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_chatHotLevel&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isFriendDontDisturb&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;10&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_magicFontOpenFlagUpdateTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_onlineMusicRemainingTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_needShowFriendTreeAnimation&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_onlineMusicEndTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_interactiveSignMode&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_friendTreeLastChatTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_richOnlineState&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;5&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_ctType&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_needShipAnimation&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_friendTreePreLevel&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_vipInfoDic&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;15&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_intimateRelationFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_network&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_lastGetCustomStateTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;20&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_enableQSL&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_QQRobert&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;3&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_avatarIdUpdateTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isMatchRealNick&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_shouldShowCustomState&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_status&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;6&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_magicFontType&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_cSpecialFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;3&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_specialCareZone&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_qzoneLevel&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_gameAppId&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_createTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_nameplateVipType&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_eIconType&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isShortcutEnable&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;2&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_memberLevel&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isFriendMask&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isMatchUIN&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_friendTreeFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_loverKeyFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_age&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_fuin&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;13&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_abiFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_priority&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_locationAbility&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;3&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_matchRange&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;21&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_loverKeyLevel&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_intimateRelationLastChatTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_topIdentityInfo&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;10&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_fontIdUpdateTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_lastGetOnlineInfoTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;18&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isEnglishName&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_qslCreateTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_loverKeyTransFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_gameNameplateId&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_notiNoVibrate&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_enableYQHType&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_praiseHotLevel&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_lastKeyTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_praiseHotDays&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_loverFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_avatarId&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_newBoatLevel&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_groupId&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_lastLoginClient&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_extendNameplateId&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_faceAddonId&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isBlockedMask&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_onlineMusicSourceType&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_lastPraiseTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_colorRingId&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isShortcutEnableUpdate&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isBlockMask&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_dstUin&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_terminalDescription&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;17&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_customStateDescription&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;17&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_gameRank&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_intimateRelationLevel&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_friendTreeContinuityDays&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_qzoneDays&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isRemark&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_holdFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_grayNameplateFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;$class&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;23&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_notiNeedHideSession&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isFriendSetTop&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;10&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_vipFontId&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_nick&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;12&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_lastChatTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_onlineMusicTotalTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isSpecialCareOpen&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_pendantIdUpdateTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_head&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;8&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_sqq&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;3&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isMsfUser&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;3&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_maxGetCustomStateInterval&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;19&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isQimFriend&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_chatHotDays&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_magicFontTypeUpdateTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_supportVideo&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_isWifi&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_notiNoSound&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_lastQzoneTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_recommendHoldFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_friendTreeNotifyTime&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_bNotShowNetWorkIcon&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_gameIconShowFlag&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_notiNoPreview&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;4&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_maxGetOnlineInfoInterval&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;19&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_lastLoginType&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;7&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_newBoatDays&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_friendTreeLevel&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;11&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_sig&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;14&lt;/integer&gt;
			&lt;/dict&gt;
			&lt;key&gt;_propertyMD5&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;22&lt;/integer&gt;
			&lt;/dict&gt;
		&lt;/dict&gt;
		&lt;true/&gt;
		&lt;integer&gt;0&lt;/integer&gt;
		&lt;false/&gt;
		&lt;integer&gt;65535&lt;/integer&gt;
		&lt;integer&gt;20&lt;/integer&gt;
		&lt;integer&gt;0&lt;/integer&gt;
		&lt;integer&gt;201&lt;/integer&gt;
		&lt;integer&gt;2&lt;/integer&gt;
		&lt;integer&gt;-1&lt;/integer&gt;
		&lt;integer&gt;0&lt;/integer&gt;
		&lt;string&gt;10001&lt;/string&gt;
		&lt;string&gt;1000194&lt;/string&gt;
		&lt;string&gt;U&lt;/string&gt;
		&lt;dict&gt;
			&lt;key&gt;NS.keys&lt;/key&gt;
			&lt;array&gt;
			&lt;/array&gt;
			&lt;key&gt;NS.objects&lt;/key&gt;
			&lt;array&gt;
			&lt;/array&gt;
			&lt;key&gt;$class&lt;/key&gt;
			&lt;dict&gt;
				&lt;key&gt;CF$UID&lt;/key&gt;
				&lt;integer&gt;16&lt;/integer&gt;
			&lt;/dict&gt;
		&lt;/dict&gt;
		&lt;dict&gt;
			&lt;key&gt;$classname&lt;/key&gt;
			&lt;string&gt;NSDictionary&lt;/string&gt;
			&lt;key&gt;$classes&lt;/key&gt;
			&lt;array&gt;
				&lt;string&gt;NSDictionary&lt;/string&gt;
				&lt;string&gt;NSObject&lt;/string&gt;
			&lt;/array&gt;
		&lt;/dict&gt;
		&lt;string&gt;&lt;/string&gt;
		&lt;real&gt;1682211584.541188&lt;/real&gt;
		&lt;real&gt;0.000000&lt;/real&gt;
		&lt;real&gt;1682211584.541188&lt;/real&gt;
		&lt;data&gt;
		AAAAAAAAAAAAAAAAAAAAAA==
		&lt;/data&gt;
		&lt;string&gt;&lt;/string&gt;
		&lt;dict&gt;
			&lt;key&gt;$classname&lt;/key&gt;
			&lt;string&gt;QQFriendModel&lt;/string&gt;
			&lt;key&gt;$classes&lt;/key&gt;
			&lt;array&gt;
				&lt;string&gt;QQFriendModel&lt;/string&gt;
				&lt;string&gt;QQModel&lt;/string&gt;
				&lt;string&gt;NSObject&lt;/string&gt;
			&lt;/array&gt;
		&lt;/dict&gt;
	&lt;/array&gt;
&lt;/dict&gt;
&lt;/plist&gt;</pre><p><br/></p><p><br/></p><p>可以看到里面有很多CF$UID这种UID类型的key值需要重新匹配具体内容，还涉及到NSDictionary等对象，通过类解析后的json结构是：<br/></p><pre class="brush:plain;toolbar:false">{
&nbsp;&nbsp;&nbsp;&nbsp;&quot;$class&quot;&nbsp;:&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;$classes&quot;&nbsp;:&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;QQFriendModel&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;QQModel&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;NSObject&quot;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;$classname&quot;&nbsp;:&nbsp;&quot;QQFriendModel&quot;
&nbsp;&nbsp;&nbsp;&nbsp;},
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_QQRobert&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_abiFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_age&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_avatarId&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_avatarIdUpdateTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_bNotShowNetWorkIcon&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_cSpecialFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_chatHotDays&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_chatHotLevel&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_closeDays&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_closeLevel&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_colorRingId&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_createTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_ctType&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_customStateDescription&quot;&nbsp;:&nbsp;&quot;&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_dstUin&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_eIconType&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_enableQSL&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_enableYQHType&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_extendNameplateId&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_faceAddonId&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_fontIdUpdateTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_friendTreeContinuityDays&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_friendTreeFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_friendTreeLastChatTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_friendTreeLevel&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_friendTreeNotifyTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_friendTreePreLevel&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_fuin&quot;&nbsp;:&nbsp;&quot;1000194&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_gameAppId&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_gameIconShowFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_gameLastLoginTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_gameNameplateId&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_gameRank&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_grayNameplateFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_groupId&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_head&quot;&nbsp;:&nbsp;201,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_holdFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_interactiveSignMode&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_intimateRelationDark&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_intimateRelationFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_intimateRelationLastChatTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_intimateRelationLevel&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isBlockMask&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isBlockedMask&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isEnglishName&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isFriendDontDisturb&quot;&nbsp;:&nbsp;18446744073709551615,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isFriendMask&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isFriendSetTop&quot;&nbsp;:&nbsp;18446744073709551615,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isMatchRealNick&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isMatchUIN&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isMsfUser&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isQimFriend&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isRemark&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isShortcutEnable&quot;&nbsp;:&nbsp;1,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isShortcutEnableUpdate&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isSpecialCareOpen&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_isWifi&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_lastChatTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_lastGetCustomStateTime&quot;&nbsp;:&nbsp;1682211584.541188,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_lastGetOnlineInfoTime&quot;&nbsp;:&nbsp;1682211584.541188,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_lastKeyTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_lastLoginClient&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_lastLoginType&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_lastPraiseTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_lastQzoneTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_locationAbility&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_loverFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_loverKeyFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_loverKeyLevel&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_loverKeyTransFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_magicFontOpenFlagUpdateTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_magicFontType&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_magicFontTypeUpdateTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_matchRange&quot;&nbsp;:&nbsp;&quot;AAAAAAAAAAAAAAAAAAAAAA==&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_maxGetCustomStateInterval&quot;&nbsp;:&nbsp;0.0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_maxGetOnlineInfoInterval&quot;&nbsp;:&nbsp;0.0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_memberLevel&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_nameplateVipType&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_needShipAnimation&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_needShowFriendTreeAnimation&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_network&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_newBoatDays&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_newBoatLevel&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_nick&quot;&nbsp;:&nbsp;&quot;10001&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_notiNeedHideSession&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_notiNoPreview&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_notiNoSound&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_notiNoVibrate&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_onlineMusicEndTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_onlineMusicRemainingTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_onlineMusicSourceType&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_onlineMusicTotalTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_pendantIdUpdateTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_praiseHotDays&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_praiseHotLevel&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_priority&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_propertyMD5&quot;&nbsp;:&nbsp;&quot;&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_qslCreateTime&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_qzoneDays&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_qzoneLevel&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_recommendHoldFlag&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_richOnlineState&quot;&nbsp;:&nbsp;65535,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_sex&quot;&nbsp;:&nbsp;2,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_shouldShowCustomState&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_sig&quot;&nbsp;:&nbsp;&quot;U&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_specialCareZone&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_sqq&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_status&quot;&nbsp;:&nbsp;20,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_supportVideo&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_terminalDescription&quot;&nbsp;:&nbsp;&quot;&quot;,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_topIdentityInfo&quot;&nbsp;:&nbsp;18446744073709551615,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_vipFontId&quot;&nbsp;:&nbsp;0,
&nbsp;&nbsp;&nbsp;&nbsp;&quot;_vipInfoDic&quot;&nbsp;:&nbsp;null
}</pre><p><br/></p><p>比如需要解析并读取&quot;_richOnlineState&quot;字段的值，解析示例代码：</p><pre class="brush:cpp;toolbar:false">CPlistObject&nbsp;mPlist;
mPlist.OpenPlistFile(&quot;demo.plist&quot;);
uint32_t&nbsp;v&nbsp;=&nbsp;mPlist[&quot;_richOnlineState&quot;].asInt();</pre><p style="text-wrap: wrap;"><br/></p><p style="text-wrap: wrap;">嗯，非常简单。最后读取的时候，记得校验字段是否存在，否则jsoncpp可能会抛异常。</p><p style="text-wrap: wrap;"><br/></p><p style="text-wrap: wrap;"><br/></p><p><br/></p>]]></description><category>C/C++代码</category><comments>http://www.fenlog.com/post/130.html#comment</comments><wfw:commentRss>http://www.fenlog.com/feed.asp?cmt=130</wfw:commentRss></item><item><title>几种地图经纬度坐标系转换</title><author>345382462@qq.com (blackfeather)</author><link>http://www.fenlog.com/post/129.html</link><pubDate>Thu, 08 Sep 2022 18:23:33 +0800</pubDate><guid>http://www.fenlog.com/post/129.html</guid><description><![CDATA[<p><br/></p><p>wgs84：GPS使用的坐标系</p><p>gcj02：中国国家测绘局制订的地理信息系统的坐标系统，是在WGS84经纬度的基础上执行加密算法而成。因为GPS得到的经纬度直接在 GCJ-02 坐标系下会定位到错误的地点，有种到了火星的感觉，因此在坊间也将 GCJ-02 戏称为火星坐标系。高德地图、腾讯地图均使用的此坐标系。</p><p>bd09：百度地图使用的，在gcj02基础上又做了一次转换。<br/></p><p><br/></p><div><table><thead><tr class="firstRow"><th style="text-align:center">地图</th><th style="text-align:center">大陆/港/澳</th><th style="text-align:center">台湾省</th><th style="text-align:center">海外</th></tr></thead><tbody><tr><td style="text-align: center; word-break: break-all;"><p>高德</p><p>腾讯</p></td><td style="text-align:center">GCJ-02</td><td style="text-align:center">WGS84</td><td style="text-align:center">WGS84</td></tr><tr><td style="text-align:center">Google</td><td style="text-align:center">GCJ-02</td><td style="text-align:center">WGS84</td><td style="text-align:center">WGS84</td></tr><tr><td style="text-align:center">百度</td><td style="text-align:center">BD-09 / GCJ-02</td><td style="text-align:center">BD-09 / GCJ-02</td><td style="text-align:center">WGS84</td></tr></tbody></table></div><p><br/></p><p><br/></p><pre class="brush:cpp;toolbar:false">//由于坐标系名词比较拗口，注释写的就通俗易懂了

//百度转高德坐标系
void&nbsp;bd09togcj02(double&nbsp;bd_lat,&nbsp;double&nbsp;bd_lon,&nbsp;double&amp;&nbsp;out_lat,&nbsp;double&amp;&nbsp;out_lon);
//高德转百度坐标系
void&nbsp;gcj02tobd09(double&nbsp;gcj_lat,&nbsp;double&nbsp;gcj_lon,&nbsp;double&amp;&nbsp;out_lat,&nbsp;double&amp;&nbsp;out_lon);
//高德转wgs84
void&nbsp;gcj02towgs84(double&nbsp;gcj_lat,&nbsp;double&nbsp;gcj_lon,&nbsp;double&amp;&nbsp;out_lat,&nbsp;double&amp;&nbsp;out_lon);
//wgs84转高德
void&nbsp;wgs84togcj02(double&nbsp;wgs_lat,&nbsp;double&nbsp;wgs_lon,&nbsp;double&amp;&nbsp;out_lat,&nbsp;double&amp;&nbsp;out_lon);


double&nbsp;x_PI&nbsp;=&nbsp;3.14159265358979323846&nbsp;*&nbsp;3000.0&nbsp;/&nbsp;180.0;
double&nbsp;PI&nbsp;=&nbsp;3.1415926535897932384626;
double&nbsp;a&nbsp;=&nbsp;6378245.0;
double&nbsp;ee&nbsp;=&nbsp;0.00669342162296594323;

bool&nbsp;outof_China(double&nbsp;lon,&nbsp;double&nbsp;lat)
{
	return(lon&nbsp;&lt;&nbsp;72.004&nbsp;||&nbsp;lon&gt;137.8374&nbsp;||&nbsp;lat&nbsp;&lt;&nbsp;0.8293&nbsp;||&nbsp;lat&nbsp;&gt;55.8271&nbsp;||&nbsp;false);
}
double&nbsp;translate_lon(double&nbsp;lon,&nbsp;double&nbsp;lat)
{
	double&nbsp;ret&nbsp;=&nbsp;300.0&nbsp;+&nbsp;lon&nbsp;+&nbsp;2.0&nbsp;*&nbsp;lat&nbsp;+&nbsp;0.1&nbsp;*&nbsp;lon&nbsp;*&nbsp;lon&nbsp;+&nbsp;0.1&nbsp;*&nbsp;lon&nbsp;*&nbsp;lat&nbsp;+&nbsp;0.1&nbsp;*&nbsp;sqrt(abs(lon));
	ret&nbsp;+=&nbsp;(20.0&nbsp;*&nbsp;sin(6.0&nbsp;*&nbsp;lon&nbsp;*&nbsp;PI)&nbsp;+&nbsp;20.0&nbsp;*&nbsp;sin(2.0&nbsp;*&nbsp;lon&nbsp;*&nbsp;PI))&nbsp;*&nbsp;2.0&nbsp;/&nbsp;3.0;
	ret&nbsp;+=&nbsp;(20.0&nbsp;*&nbsp;sin(lon&nbsp;*&nbsp;PI)&nbsp;+&nbsp;40.0&nbsp;*&nbsp;sin(lon&nbsp;/&nbsp;3.0&nbsp;*&nbsp;PI))&nbsp;*&nbsp;2.0&nbsp;/&nbsp;3.0;
	ret&nbsp;+=&nbsp;(150&nbsp;*&nbsp;sin(lon&nbsp;/&nbsp;12.0&nbsp;*&nbsp;PI)&nbsp;+&nbsp;300.0&nbsp;*&nbsp;sin(lon&nbsp;/&nbsp;30.0&nbsp;*&nbsp;PI))&nbsp;*&nbsp;2.0&nbsp;/&nbsp;3.0;
	return&nbsp;ret;
}
double&nbsp;translate_lat(double&nbsp;lon,&nbsp;double&nbsp;lat)
{
	double&nbsp;ret&nbsp;=&nbsp;-100&nbsp;+&nbsp;2.0&nbsp;*&nbsp;lon&nbsp;+&nbsp;3.0&nbsp;*&nbsp;lat&nbsp;+&nbsp;0.2&nbsp;*&nbsp;lat&nbsp;*&nbsp;lat&nbsp;+&nbsp;0.1&nbsp;*&nbsp;lon&nbsp;*&nbsp;lat&nbsp;+&nbsp;0.2&nbsp;*&nbsp;sqrt((abs(lon)));
	ret&nbsp;+=&nbsp;(20.0&nbsp;*&nbsp;sin(6.0&nbsp;*&nbsp;lon&nbsp;*&nbsp;PI)&nbsp;+&nbsp;20&nbsp;*&nbsp;sin(2.0&nbsp;*&nbsp;lon&nbsp;*&nbsp;PI))&nbsp;*&nbsp;2.0&nbsp;/&nbsp;3.0;
	ret&nbsp;+=&nbsp;(20.0&nbsp;*&nbsp;sin(lat&nbsp;*&nbsp;PI)&nbsp;+&nbsp;40.0&nbsp;*&nbsp;sin(lat&nbsp;/&nbsp;3.0&nbsp;*&nbsp;PI))&nbsp;*&nbsp;2.0&nbsp;/&nbsp;3.0;
	ret&nbsp;+=&nbsp;(160.0&nbsp;*&nbsp;sin(lat&nbsp;/&nbsp;12.0&nbsp;*&nbsp;PI)&nbsp;+&nbsp;320.0&nbsp;*&nbsp;sin(lat&nbsp;/&nbsp;30.0&nbsp;*&nbsp;PI))&nbsp;*&nbsp;2.0&nbsp;/&nbsp;3.0;
	return&nbsp;ret;
}
void&nbsp;bd09togcj02(double&nbsp;bd_lat,&nbsp;double&nbsp;bd_lon,&nbsp;double&amp;&nbsp;out_lat,&nbsp;double&amp;&nbsp;out_lon)
{
	double&nbsp;x&nbsp;=&nbsp;bd_lon&nbsp;-&nbsp;0.0065;
	double&nbsp;y&nbsp;=&nbsp;bd_lat&nbsp;-&nbsp;0.006;
	double&nbsp;z&nbsp;=&nbsp;sqrt(x&nbsp;*&nbsp;x&nbsp;+&nbsp;y&nbsp;*&nbsp;y)&nbsp;-&nbsp;0.00002&nbsp;*&nbsp;sin(y&nbsp;*&nbsp;x_PI);
	double&nbsp;theta&nbsp;=&nbsp;atan2(y,&nbsp;x)&nbsp;-&nbsp;0.000003&nbsp;*&nbsp;cos(x&nbsp;*&nbsp;x_PI);
	out_lon&nbsp;=&nbsp;z&nbsp;*&nbsp;cos(theta);
	out_lat&nbsp;=&nbsp;z&nbsp;*&nbsp;sin(theta);
}
void&nbsp;gcj02tobd09(double&nbsp;gcj_lat,&nbsp;double&nbsp;gcj_lon,&nbsp;double&amp;&nbsp;out_lat,&nbsp;double&amp;&nbsp;out_lon)
{
	double&nbsp;z&nbsp;=&nbsp;sqrt(gcj_lon&nbsp;*&nbsp;gcj_lon&nbsp;+&nbsp;gcj_lat&nbsp;*&nbsp;gcj_lat)&nbsp;+&nbsp;0.00002&nbsp;*&nbsp;sin(gcj_lat&nbsp;*&nbsp;x_PI);
	double&nbsp;theta&nbsp;=&nbsp;atan2(gcj_lat,&nbsp;gcj_lon)&nbsp;+&nbsp;0.000003&nbsp;*&nbsp;cos(gcj_lon&nbsp;*&nbsp;x_PI);
	out_lon&nbsp;=&nbsp;z&nbsp;*&nbsp;cos(theta)&nbsp;+&nbsp;0.0065;
	out_lat&nbsp;=&nbsp;z&nbsp;*&nbsp;sin(theta)&nbsp;+&nbsp;0.006;
}
void&nbsp;gcj02towgs84(double&nbsp;gcj_lat,&nbsp;double&nbsp;gcj_lon,&nbsp;double&amp;&nbsp;out_lat,&nbsp;double&amp;&nbsp;out_lon)
{
	if&nbsp;(outof_China(gcj_lon,&nbsp;gcj_lat))
	{
		out_lon&nbsp;=&nbsp;gcj_lon;
		out_lat&nbsp;=&nbsp;gcj_lat;
		return;
	}
	
	double&nbsp;dlat&nbsp;=&nbsp;translate_lat(gcj_lon&nbsp;-&nbsp;105.0,&nbsp;gcj_lat&nbsp;-&nbsp;35.0);
	double&nbsp;dlon&nbsp;=&nbsp;translate_lon(gcj_lon&nbsp;-&nbsp;105.0,&nbsp;gcj_lat&nbsp;-&nbsp;35.0);
	double&nbsp;radlat&nbsp;=&nbsp;gcj_lat&nbsp;/&nbsp;180.0&nbsp;*&nbsp;PI;
	double&nbsp;magic&nbsp;=&nbsp;sin(radlat);
	magic&nbsp;=&nbsp;1&nbsp;-&nbsp;ee&nbsp;*&nbsp;magic&nbsp;*&nbsp;magic;
	double&nbsp;squrtmagic&nbsp;=&nbsp;sqrt(magic);
	dlon&nbsp;=&nbsp;(dlon&nbsp;*&nbsp;180.0)&nbsp;/&nbsp;(a&nbsp;/&nbsp;squrtmagic&nbsp;*&nbsp;cos(radlat)&nbsp;*&nbsp;PI);
	dlat&nbsp;=&nbsp;(dlat&nbsp;*&nbsp;180.0)&nbsp;/&nbsp;((a&nbsp;*&nbsp;(1&nbsp;-&nbsp;ee))&nbsp;/&nbsp;(magic&nbsp;*&nbsp;squrtmagic)&nbsp;*&nbsp;PI);
	out_lon&nbsp;=&nbsp;gcj_lon&nbsp;-&nbsp;dlon;
	out_lat&nbsp;=&nbsp;gcj_lat&nbsp;-&nbsp;dlat;
}
void&nbsp;wgs84togcj02(double&nbsp;wgs_lat,&nbsp;double&nbsp;wgs_lon,&nbsp;double&amp;&nbsp;out_lat,&nbsp;double&amp;&nbsp;out_lon)
{
	if&nbsp;(outof_China(wgs_lon,&nbsp;wgs_lat))
	{
		out_lon&nbsp;=&nbsp;wgs_lon;
		out_lat&nbsp;=&nbsp;wgs_lat;
		return;
	}
	
	double&nbsp;dlat&nbsp;=&nbsp;translate_lat(wgs_lon&nbsp;-&nbsp;105.0,&nbsp;wgs_lat&nbsp;-&nbsp;35.0);
	double&nbsp;dlon&nbsp;=&nbsp;translate_lon(wgs_lon&nbsp;-&nbsp;105.0,&nbsp;wgs_lat&nbsp;-&nbsp;35.0);
	double&nbsp;radlat&nbsp;=&nbsp;wgs_lat&nbsp;/&nbsp;180.0&nbsp;*&nbsp;PI;
	double&nbsp;magic&nbsp;=&nbsp;sin(radlat);
	magic&nbsp;=&nbsp;1&nbsp;-&nbsp;ee&nbsp;*&nbsp;magic&nbsp;*&nbsp;magic;
	double&nbsp;squrtmagic&nbsp;=&nbsp;sqrt(magic);
	dlon&nbsp;=&nbsp;(dlon&nbsp;*&nbsp;180.0)&nbsp;/&nbsp;(a&nbsp;/&nbsp;squrtmagic&nbsp;*&nbsp;cos(radlat)&nbsp;*&nbsp;PI);
	dlat&nbsp;=&nbsp;(dlat&nbsp;*&nbsp;180.0)&nbsp;/&nbsp;((a&nbsp;*&nbsp;(1&nbsp;-&nbsp;ee))&nbsp;/&nbsp;(magic&nbsp;*&nbsp;squrtmagic)&nbsp;*&nbsp;PI);
	out_lon&nbsp;=&nbsp;wgs_lon&nbsp;+&nbsp;dlon;
	out_lat&nbsp;=&nbsp;wgs_lat&nbsp;+&nbsp;dlat;
}</pre><p><br/></p>]]></description><category>C/C++代码</category><comments>http://www.fenlog.com/post/129.html#comment</comments><wfw:commentRss>http://www.fenlog.com/feed.asp?cmt=129</wfw:commentRss></item><item><title>任务队列和线程池队列(C++11)</title><author>345382462@qq.com (blackfeather)</author><link>http://www.fenlog.com/post/128.html</link><pubDate>Tue, 24 Aug 2021 12:23:34 +0800</pubDate><guid>http://www.fenlog.com/post/128.html</guid><description><![CDATA[<p><br/></p><p>任务队列可以认为是执行同一个方法来处理数据的队列，指定回调函数。</p><p>线程池就是先开辟好多个线程，然后将要执行的方法+参数丢到线程池中，支持返回值获取。</p><p><br/></p><p>均依赖了无锁队列库&nbsp;<span style="color: rgb(85, 85, 85); font-family: &quot;Microsoft Yahei&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif; font-size: 15px; background-color: rgb(255, 255, 255);"><a href="https://github.com/cameron314/concurrentqueue" _src="https://github.com/cameron314/concurrentqueue">https://github.com/cameron314/concurrentqueue</a> </span></p><p>本来不想依赖三方组件的，但是测试发现这个库效率是真的高，比带锁的队列快非常多。</p><p>而且vs中的std::queue是有BUG的，pop后不释放内存，也有一定的隐患（急死强迫症）</p><p><br/></p><p>任务队列库TaskQueue：</p><pre class="brush:cpp;toolbar:false">#pragma&nbsp;once

#include&nbsp;&lt;functional&gt;
#include&nbsp;&lt;vector&gt;
#include&nbsp;&lt;thread&gt;
#include&nbsp;&lt;future&gt;
#include&nbsp;&lt;atomic&gt;
#include&nbsp;&lt;chrono&gt;
#include&nbsp;&lt;mutex&gt;

#include&nbsp;&quot;queue/blockingconcurrentqueue.h&quot;&nbsp;//for&nbsp;tsBlockQuque

template&nbsp;&lt;class&nbsp;T,&nbsp;class&nbsp;P&nbsp;=&nbsp;void*&gt;
class&nbsp;CTaskQueue
{
private:
	typedef&nbsp;std::function&lt;void(P&nbsp;&amp;param)&gt;&nbsp;functionTaskInit;
	typedef&nbsp;std::function&lt;void(P&nbsp;&amp;param)&gt;&nbsp;functionTaskExit;
	typedef&nbsp;std::function&lt;bool(T&nbsp;&amp;data)&gt;&nbsp;functionTaskCallback;
	typedef&nbsp;std::function&lt;bool(T&nbsp;&amp;data,&nbsp;P&nbsp;&amp;param)&gt;&nbsp;functionTaskExCallback;
	//退出的时候用的回调
	typedef&nbsp;std::function&lt;void(uint64_t,&nbsp;uint64_t)&gt;&nbsp;functionWaitCallback;

	//退出标记
	bool&nbsp;m_bExit;
	//检测队列的等待时间，毫秒
	int&nbsp;m_nWaitTime;
	//延时模式开关
	bool&nbsp;m_bDelayMode;
	//延时模式下，启动新工作线程需要加锁
	std::mutex&nbsp;m_mtStartWorkThread;
	//最大线程数
	size_t&nbsp;m_nMaxThreadCount;
	//添加的总数量的数量
	std::atomic&lt;uint64_t&gt;&nbsp;m_nTotalCount;
	//已处理完毕的数量
	std::atomic&lt;uint64_t&gt;&nbsp;m_nProcessedCount;
	//任务队列
	tsBlockQuque&nbsp;&lt;T&gt;&nbsp;m_taskQueue;

	//单参数的回调
	functionTaskCallback&nbsp;m_TaskCallBack;

	//带扩展参数的回调
	functionTaskInit&nbsp;m_TaskInitCallBack;
	functionTaskExit&nbsp;m_TaskExitCallBack;
	functionTaskExCallback&nbsp;m_TaskExCallBack;

	//工作线程
	std::vector&nbsp;&lt;std::future&lt;int&gt;&gt;&nbsp;m_arrWorkThreads;

	//启动工作线程
	void&nbsp;_StartWorkThread(size_t&nbsp;nThreadCount)
	{
		std::lock_guard&lt;std::mutex&gt;&nbsp;lock(m_mtStartWorkThread);

		if&nbsp;(m_arrWorkThreads.size()&nbsp;&gt;=&nbsp;m_nMaxThreadCount)
			return;
		
		for&nbsp;(size_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nThreadCount;&nbsp;i++)
		{
			m_arrWorkThreads.emplace_back(std::async(std::launch::async,&nbsp;[this]()-&gt;int{

				P&nbsp;param;
				if&nbsp;(m_TaskInitCallBack)
					m_TaskInitCallBack(param);

				while&nbsp;(true)
				{
					T&nbsp;data;
					if&nbsp;(m_taskQueue.wait_dequeue_timed(data,&nbsp;std::chrono::milliseconds(m_nWaitTime)))
					{
						//找到一组数据&nbsp;回调
						if&nbsp;(m_TaskCallBack)
						{
							if&nbsp;(!m_TaskCallBack(data))
							{
								//处理失败了&nbsp;重新丢回队列里面去
								m_taskQueue.enqueue(data);
								continue;
							}
						}
						else&nbsp;if&nbsp;(m_TaskExCallBack)
						{
							if&nbsp;(!m_TaskExCallBack(data,&nbsp;param))
							{
								//处理失败了&nbsp;重新丢回队列里面去
								m_taskQueue.enqueue(data);
								continue;
							}
						}

						m_nProcessedCount++;
						continue;
					}

					//结束标记并且队列没有数据了&nbsp;返回
					if&nbsp;(m_bExit)
						break;
				}

				if&nbsp;(m_TaskExitCallBack)
					m_TaskExitCallBack(param);

				return&nbsp;0;
			}));
		}

		//创建完毕工作线程后，重置一下标记
		if&nbsp;(m_bDelayMode&nbsp;&amp;&amp;&nbsp;m_arrWorkThreads.size()&nbsp;&gt;=&nbsp;m_nMaxThreadCount)
			m_bDelayMode&nbsp;=&nbsp;false;
	}
public:

	//禁止拷贝和移动
	CTaskQueue(const&nbsp;CTaskQueue&amp;)&nbsp;=&nbsp;delete;
	CTaskQueue&amp;&nbsp;operator=(const&nbsp;CTaskQueue&amp;)&nbsp;=&nbsp;delete;

	/*
	构造函数：
		bDelayMode:&nbsp;延时模式
			true:延时启动工作线程，有需要的时候启动
			false:立即启动工作线程
		nWaitTime:等待队列的超时时间，毫秒
	*/
	CTaskQueue(bool&nbsp;bDelayMode&nbsp;=&nbsp;false,&nbsp;int&nbsp;nWaitTime&nbsp;=&nbsp;200)
	{
		m_bExit&nbsp;=&nbsp;false;
		m_nTotalCount&nbsp;=&nbsp;0;
		m_nProcessedCount&nbsp;=&nbsp;0;
		m_bDelayMode&nbsp;=&nbsp;bDelayMode;
		m_nWaitTime&nbsp;=&nbsp;nWaitTime;
	}
	~CTaskQueue()
	{
		m_bExit&nbsp;=&nbsp;true;

		for&nbsp;(auto&amp;&nbsp;fu&nbsp;:&nbsp;m_arrWorkThreads)
		{
			try
			{
				fu.get();
			}
			catch&nbsp;(...){}
		}
	}

	/*
	简单的回调方法的初始化
	参数：
		线程数
		回调方法
	*/
	void&nbsp;InitTaskQueue(size_t&nbsp;nMaxThreadCount,&nbsp;functionTaskCallback&nbsp;fnTaskCallback)
	{
		m_TaskCallBack&nbsp;=&nbsp;fnTaskCallback;
		m_TaskExCallBack&nbsp;=&nbsp;nullptr;
		m_TaskInitCallBack&nbsp;=&nbsp;nullptr;
		m_TaskExitCallBack&nbsp;=&nbsp;nullptr;
		m_nMaxThreadCount&nbsp;=&nbsp;nMaxThreadCount;

		if&nbsp;(!m_bDelayMode)
			_StartWorkThread(nMaxThreadCount);
	}
	/*
	带队列线程初始化和销毁回调方法的初始化
	参数：
		线程数
		数据处理回调方法
		队列线程初始化回调
		队列线程销毁回调
	*/
	void&nbsp;InitTaskQueue(int&nbsp;nMaxThreadCount,&nbsp;functionTaskExCallback&nbsp;fnTaskExCallback,&nbsp;functionTaskInit&nbsp;fnTaskInitCallback,&nbsp;functionTaskExit&nbsp;fnTaskExitCallback)
	{
		m_TaskCallBack&nbsp;=&nbsp;nullptr;
		m_TaskExCallBack&nbsp;=&nbsp;fnTaskExCallback;
		m_TaskInitCallBack&nbsp;=&nbsp;fnTaskInitCallback;
		m_TaskExitCallBack&nbsp;=&nbsp;fnTaskExitCallback;
		m_nMaxThreadCount&nbsp;=&nbsp;nMaxThreadCount;

		if&nbsp;(!m_bDelayMode)
			_StartWorkThread(nMaxThreadCount);
	}

	//添加数据
	void&nbsp;AddData(const&nbsp;T&nbsp;&amp;data)
	{
		if&nbsp;(m_bDelayMode)
		{
			//延时模式下，如果任务还没有处理完毕，就增加一个工作线程
			if&nbsp;(!IsFinished()&nbsp;||&nbsp;m_arrWorkThreads.size()&nbsp;==&nbsp;0)
			{
				//内部有工作线程数检测，达到最大线程数后自动关闭延时模式
				_StartWorkThread(1);
			}
		}

		assert(m_arrWorkThreads.size());
		m_taskQueue.enqueue(data);
		m_nTotalCount++;
	}

	//获取当前工作线程数
	size_t&nbsp;GetWorkThreadCount()
	{
		return&nbsp;m_arrWorkThreads.size();
	}

	//读取当前队列中待处理的数量
	uint64_t&nbsp;GetQueueSize()
	{
		return&nbsp;m_taskQueue.size_approx();
	}

	//读取已经处理过的数量
	uint64_t&nbsp;GetProcessedCount()
	{
		return&nbsp;m_nProcessedCount;
	}

	//读取所有加入过队列的数量
	uint64_t&nbsp;GetTotalCount()
	{
		return&nbsp;m_nTotalCount;
	}

	//查询队列是否处理完毕&nbsp;&nbsp;立即返回
	bool&nbsp;IsFinished()
	{
		if&nbsp;(m_nTotalCount&nbsp;==&nbsp;m_nProcessedCount&nbsp;&amp;&amp;&nbsp;GetQueueSize()&nbsp;==&nbsp;0)
			return&nbsp;true;

		return&nbsp;false;
	}

	//等待队列结束&nbsp;&nbsp;阻塞执行
	void&nbsp;WaitForFinish(int&nbsp;nCheckms&nbsp;=&nbsp;100,&nbsp;functionWaitCallback&nbsp;fnWaitCallback&nbsp;=&nbsp;nullptr)
	{
		printf(&quot;Wait&nbsp;for&nbsp;finish...\n&quot;);

		while&nbsp;(!IsFinished())
		{
			if&nbsp;(fnWaitCallback)
				fnWaitCallback(m_nProcessedCount,&nbsp;m_nTotalCount);

			std::this_thread::sleep_for(std::chrono::milliseconds(nCheckms));
		}

		if&nbsp;(fnWaitCallback)
			fnWaitCallback(m_nProcessedCount,&nbsp;m_nTotalCount);

		printf(&quot;All&nbsp;finished.&nbsp;\n&quot;);
	}
};</pre><p><br/></p><p>示例代码：</p><pre class="brush:cpp;toolbar:false">&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;//
&nbsp;&nbsp;&nbsp;&nbsp;CTaskQueue&lt;int,&nbsp;int&gt;&nbsp;taskTester;&nbsp;&nbsp;//第二个int可忽略
	
	//单一回调
	taskTester.InitTaskQueue(8,&nbsp;[](int&nbsp;data)-&gt;bool{
		printf(&quot;task:%d\n&quot;,&nbsp;data);
		return&nbsp;true;&nbsp;//返回false会重新插回队列中
	}
	
	//多个回调的初始化方法
	taskTester.InitTaskQueue(8,&nbsp;[](int&nbsp;data,&nbsp;int&nbsp;&amp;param)-&gt;bool{
		printf(&quot;task:%d,&nbsp;param:%d\n&quot;,&nbsp;data,&nbsp;param);
		return&nbsp;true;
	},
	[](int&nbsp;&amp;param){
		param&nbsp;=&nbsp;GetCurrentThreadId();
		printf(&quot;task&nbsp;work&nbsp;thread&nbsp;init:%d&nbsp;\n&quot;,&nbsp;param);
	},
	[](int&nbsp;&amp;param){
		printf(&quot;task&nbsp;work&nbsp;thread&nbsp;exit:%d&nbsp;\n&quot;,&nbsp;param);
	});

	for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;10000;&nbsp;i++)
	{
		taskTester.AddData(i);
	}

	taskTester.WaitforFinish(100,&nbsp;[](uint64_t&nbsp;processed,&nbsp;uint64_t&nbsp;total){
		uint64_t&nbsp;ret&nbsp;=&nbsp;(processed&nbsp;*&nbsp;100)&nbsp;/&nbsp;total;
		printf(&quot;processed:%I64d,&nbsp;total:%I64d,&nbsp;%d%%&nbsp;\n&quot;,&nbsp;processed,&nbsp;total,&nbsp;(int)ret);
	});</pre><p><br/></p><p><br/></p><p><br/></p><p>线程池是用之前的帖子中的线程池修改的，增加了几个方法，修正了vs下的BUG 。。。</p><pre class="brush:cpp;toolbar:false">#pragma&nbsp;once

#include&nbsp;&lt;vector&gt;
#include&nbsp;&lt;memory&gt;
#include&nbsp;&lt;thread&gt;
#include&nbsp;&lt;future&gt;
#include&nbsp;&lt;functional&gt;
#include&nbsp;&lt;stdexcept&gt;
#include&nbsp;&lt;atomic&gt;
#include&nbsp;&lt;chrono&gt;
#include&nbsp;&lt;mutex&gt;

#include&nbsp;&quot;queue/blockingconcurrentqueue.h&quot;&nbsp;//for&nbsp;tsBlockQuque

class&nbsp;ThreadPool&nbsp;
{
private:
	//退出时回调方法声明
	typedef&nbsp;std::function&lt;void(uint64_t,&nbsp;uint64_t)&gt;&nbsp;functionWaitCallback;

	//工作线程
	std::vector&lt;std::future&lt;int&gt;&gt;&nbsp;m_arrWorkThreads;
	//任务队列
	tsBlockQuque&nbsp;&lt;&nbsp;std::function&lt;void()&gt;&nbsp;&gt;&nbsp;m_TaskQueue;

	//退出标记
	bool&nbsp;m_bExit;
	//检测队列的等待时间，毫秒
	int&nbsp;m_nWaitTime;
	//延时模式开关
	bool&nbsp;m_bDelayMode;
	//延时模式下，启动新工作线程需要加锁
	std::mutex&nbsp;m_mtStartWorkThread;
	//最大线程数
	size_t&nbsp;m_nMaxThreadCount;
	//添加的总数量的数量
	std::atomic&lt;uint64_t&gt;&nbsp;m_nTotalCount;
	//已处理完毕的数量
	std::atomic&lt;uint64_t&gt;&nbsp;m_nProcessedCount;

	//启动工作线程
	void&nbsp;_StartWorkThread(size_t&nbsp;nThreadCount)
	{
		std::lock_guard&lt;std::mutex&gt;&nbsp;lock(m_mtStartWorkThread);

		if&nbsp;(m_arrWorkThreads.size()&nbsp;&gt;=&nbsp;m_nMaxThreadCount)
			return;

		for&nbsp;(size_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nThreadCount;&nbsp;i++)
		{
			m_arrWorkThreads.emplace_back(std::async(std::launch::async,&nbsp;[this]()-&gt;int{

				printf(&quot;thread&nbsp;pool&nbsp;thread&nbsp;start...\n&quot;);

				while&nbsp;(true)
				{
					std::function&lt;void()&gt;&nbsp;task;
					if&nbsp;(m_TaskQueue.wait_dequeue_timed(task,&nbsp;std::chrono::milliseconds(m_nWaitTime)))
					{
						task();
						m_nProcessedCount++;
						continue;
					}

					//结束标记并且队列没有数据了&nbsp;返回
					if&nbsp;(m_bExit)
						break;
				}

				printf(&quot;thread&nbsp;pool&nbsp;thread&nbsp;exit...\n&quot;);
				return&nbsp;0;
			}));
		}

		//创建完毕工作线程后，重置一下标记
		if&nbsp;(m_bDelayMode&nbsp;&amp;&amp;&nbsp;m_arrWorkThreads.size()&nbsp;&gt;=&nbsp;m_nMaxThreadCount)
			m_bDelayMode&nbsp;=&nbsp;false;
	}

public:

	//禁止拷贝和移动
	ThreadPool(const&nbsp;ThreadPool&amp;)&nbsp;=&nbsp;delete;
	ThreadPool&amp;&nbsp;operator=(const&nbsp;ThreadPool&amp;)&nbsp;=&nbsp;delete;

	/*
	构造函数
		nMaxThreadCount:最大工作线程数
		bDelayMode：延时模式
			true:延时启动工作线程，有需要的时候启动
			false:立即启动工作线程
		nWaitTime:等待队列的超时时间，毫秒
	*/
	ThreadPool(size_t&nbsp;nMaxThreadCount,&nbsp;bool&nbsp;bDelayMode&nbsp;=&nbsp;false,&nbsp;int&nbsp;nWaitTime&nbsp;=&nbsp;200)
	{
		m_nTotalCount&nbsp;=&nbsp;0;
		m_nProcessedCount&nbsp;=&nbsp;0;
		m_bExit&nbsp;=&nbsp;false;

		InitThreadPool(nMaxThreadCount,&nbsp;bDelayMode,&nbsp;nWaitTime);
	}
	ThreadPool()
	{
		m_nTotalCount&nbsp;=&nbsp;0;
		m_nProcessedCount&nbsp;=&nbsp;0;
		m_bExit&nbsp;=&nbsp;false;
	}
	~ThreadPool()
	{
		m_bExit&nbsp;=&nbsp;true;

		for&nbsp;(auto&amp;&nbsp;fu&nbsp;:&nbsp;m_arrWorkThreads)
		{
			try
			{
				fu.get();
			}
			catch&nbsp;(...){}
		}
	}

	/*
	初始化线程池，如果使用带参数的构造函数初始化，则无需调用此方法
		nMaxThreadCount:最大工作线程数
		bDelayMode：延时模式
			true:延时启动工作线程，有需要的时候启动
			false:立即启动工作线程
		nWaitTime:等待队列的超时时间，毫秒
	*/
	void&nbsp;InitThreadPool(size_t&nbsp;nMaxThreadCount,&nbsp;bool&nbsp;bDelayMode&nbsp;=&nbsp;false,&nbsp;int&nbsp;nWaitTime&nbsp;=&nbsp;200)
	{
		m_nMaxThreadCount&nbsp;=&nbsp;nMaxThreadCount;
		m_bDelayMode&nbsp;=&nbsp;bDelayMode;
		m_nWaitTime&nbsp;=&nbsp;nWaitTime;

		if&nbsp;(!m_bDelayMode)
			_StartWorkThread(nMaxThreadCount);
	}

	//添加任务
	template&lt;class&nbsp;F,&nbsp;class...&nbsp;Args&gt;
	auto&nbsp;AddTask(F&amp;&amp;&nbsp;f,&nbsp;Args&amp;&amp;...&nbsp;args)&nbsp;-&gt;&nbsp;std::future&lt;typename&nbsp;std::result_of&lt;F(Args...)&gt;::type&gt;
	{
		if&nbsp;(m_bDelayMode)
		{
			//延时模式下，如果任务还没有处理完毕，就增加一个工作线程
			if&nbsp;(!IsFinished()&nbsp;||&nbsp;m_arrWorkThreads.size()&nbsp;==&nbsp;0)
			{
				//内部有工作线程数检测，达到最大线程数后自动关闭延时模式
				_StartWorkThread(1);
			}
		}

		assert(m_arrWorkThreads.size());

		using&nbsp;return_type&nbsp;=&nbsp;typename&nbsp;std::result_of&lt;F(Args...)&gt;::type;
		auto&nbsp;task&nbsp;=&nbsp;std::make_shared&lt;&nbsp;std::packaged_task&lt;return_type()&gt;&nbsp;&gt;(std::bind(std::forward&lt;F&gt;(f),&nbsp;std::forward&lt;Args&gt;(args)...));
		std::future&lt;return_type&gt;&nbsp;res&nbsp;=&nbsp;task-&gt;get_future();
		m_TaskQueue.enqueue([task](){&nbsp;(*task)();&nbsp;});
		m_nTotalCount++;

		return&nbsp;res;
	}

	//获取当前工作线程数
	size_t&nbsp;GetWorkThreadCount()
	{
		return&nbsp;m_arrWorkThreads.size();
	}

	//获取总共插入过的任务数
	uint64_t&nbsp;GetTotalCount()
	{
		return&nbsp;m_nTotalCount;
	}

	//获取已经处理完毕的任务数
	uint64_t&nbsp;GetProcessedCount()
	{
		return&nbsp;m_nProcessedCount;
	}

	//读取当前队列中待处理的数量
	uint64_t&nbsp;GetQueueSize()
	{
		return&nbsp;m_TaskQueue.size_approx();
	}

	//判断是否已经执行完毕已经插入的任务
	bool&nbsp;IsFinished()
	{
		if&nbsp;(m_nProcessedCount&nbsp;==&nbsp;m_nTotalCount&nbsp;&amp;&amp;&nbsp;m_TaskQueue.size_approx()&nbsp;==&nbsp;0)
			return&nbsp;true;

		return&nbsp;false;
	}


	//阻塞等待队列执行完毕
	void&nbsp;WaitForFinish(int&nbsp;nCheckms&nbsp;=&nbsp;100,&nbsp;functionWaitCallback&nbsp;fnWaitCallback&nbsp;=&nbsp;nullptr)
	{
		printf(&quot;Wait&nbsp;for&nbsp;finish...\n&quot;);

		while&nbsp;(!IsFinished())
		{
			if&nbsp;(fnWaitCallback)
				fnWaitCallback(m_nProcessedCount,&nbsp;m_nTotalCount);

			std::this_thread::sleep_for(std::chrono::milliseconds(nCheckms));
		}

		if&nbsp;(fnWaitCallback)
			fnWaitCallback(m_nProcessedCount,&nbsp;m_nTotalCount);

		printf(&quot;All&nbsp;finished.&nbsp;\n&quot;);
	}
};</pre><p><br/></p><p><br/></p><p>最后说说vs神奇的BUG。。。</p><pre class="brush:cpp;toolbar:false">#include&nbsp;&lt;iostream&gt;
#include&nbsp;&lt;string&gt;
#include&nbsp;&lt;vector&gt;
#include&nbsp;&lt;thread&gt;


class&nbsp;MyClass
{
private:
	std::vector&lt;std::thread&gt;&nbsp;arrthreads;
	bool&nbsp;bexit;
public:
	MyClass()
	{
		bexit&nbsp;=&nbsp;false;
	};
	~MyClass()
	{
		bexit&nbsp;=&nbsp;true;
		for&nbsp;(auto&nbsp;&amp;x&nbsp;:&nbsp;arrthreads)
		{
			x.join();&nbsp;&nbsp;//vs编译的这里会卡死，gcc编译的正常执行
		}
	};

	void&nbsp;init()
	{
		for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;4;&nbsp;i++)
		{
			arrthreads.emplace_back([this](){

				printf(&quot;trhead&nbsp;start...\n&quot;);

				while&nbsp;(true)
				{
					if&nbsp;(bexit)
						break;

				}

				printf(&quot;trhead&nbsp;exit...\n&quot;);
			});
		}
	};
};


MyClass&nbsp;testthread;

int&nbsp;main()
{
&nbsp;&nbsp;&nbsp;&nbsp;printf(&quot;main2\n&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;testthread.init();
&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0;
}</pre><p>MyClass析构函数中让工作线程退出，毫无问题，但是vs编译的，join会卡死阻塞或者崩溃跑飞，gcc编译的无问题。<br/></p><p>各位大师有何看法。。。</p>]]></description><category>C/C++代码</category><comments>http://www.fenlog.com/post/128.html#comment</comments><wfw:commentRss>http://www.fenlog.com/feed.asp?cmt=128</wfw:commentRss></item><item><title>性能监控之PDH系列方法C++封装</title><author>345382462@qq.com (blackfeather)</author><link>http://www.fenlog.com/post/127.html</link><pubDate>Fri, 25 Jun 2021 13:19:37 +0800</pubDate><guid>http://www.fenlog.com/post/127.html</guid><description><![CDATA[<p><br/></p><p>很多应用需要监控系统资源的使用率等信息，之前零散写过很多。</p><p>近日需要读取硬盘的IO使用率，就是任务管理器中的硬盘相关信息。</p><p><img src="http://www.fenlog.com/zb_users/upload/2021/6/2021062548162753.png" style="width: 548px; height: 606px;"/></p><p><br/></p><p>读写速度很好搞定，但是这个百分比的使用率（活动时间）恶心了，最后搜索到的技术点都指向了Pdh(performance data helper)库。</p><p><br/></p><p>上手一个技术点确实有点懵逼，花了一天学习才搞明白是什么意思。</p><p><br/></p><p>这玩意是windows系统底层封装好的一个统计器，并提供了一套API可以让使用者自行调用来统计。</p><p>网上写的越来越复杂，越想描述越说不清楚，整的一脸懵逼，其实整理出来流程就5步。</p><p>直接说下最简单扼要的流程：</p><p><br/></p><p>//1.创建监控器</p><p>PdhOpenQueryA(NULL, NULL, &amp;m_hQuery);</p><p>//2.添加要监控的Counter，这个Counter是一个类似path的字符串，这个后续补充说明怎么查看，返回监控的目标的句柄</p><p>PdhAddCounterA(m_hQuery, strCounterPath.c_str(), 0, &amp;hCounter);</p><p>//3.每隔一段时间要刷新一下监控器，注意两次调用之间一定要隔开足够长的时间，官网的示例是sleep（1000），1秒</p><p>PdhCollectQueryData(m_hQuery);</p><p>//4.读取监控的Counter的值</p><p>PdhGetFormattedCounterValue(hCounter,nFmt,&amp;CounterType,&amp;mRet);</p><p>//5.不用了要关闭监控器<br/></p><p>PdhCloseQuery(m_hQuery);</p><p><br/></p><p><br/></p><p>实际原理就是先建立一个监控器，然后往这个监控器里面添加要监控的条目（Counter），允许监控的信息特别详细特别多什么CPU内存硬盘网络等等都有。然后每隔一段时间调用一下刷新监控器（PdhCollectQueryData），然后就再读取数值就得到了两次刷新之间监控的目标的数据了。</p><p>这里在强调一遍，两次调用刷新一定要间隔大一些，如果顺序连续两次调用，等于间隔时间无限小，会造成数据极为不准确。可以简单理解为计算下载速度一样，先前后两次读取下载的字节数，然后差值除以间隔时间就是速度，如果间隔时间太小就会造成不精确。</p><p><br/></p><p>最后是关键信息，监控的Counter怎么查看，怎么得到路径。</p><p>win+R，打开perfmon.msc（性能监视器）</p><p><img src="http://www.fenlog.com/zb_users/upload/2021/6/2021062549311265.png"/></p><p>在中间空白的地方点击右键，添加计数器，会弹出来“可用计数器”的页面让你选择，这里就是系统支持的所有类型。</p><p>比如按照上述的需求要统计硬盘的使用率，那么就是下图所示：（坑爹的玩意硬盘使用率叫DiskTime，开始一直以为是DiskUsage所以没搜索到）</p><p><img src="http://www.fenlog.com/zb_users/upload/2021/6/2021062549123901.png"/></p><p>选择好后确定，就能看到底部多了一条监控Counter。</p><p><img src="http://www.fenlog.com/zb_users/upload/2021/6/2021062549219889.png"/></p><p>底部Counter的条目点击右键，属性，就能查看到具体的Counter的Path了，调用PdhAddCounterA传入的那个Path就是这个。</p><p><img src="http://www.fenlog.com/zb_users/upload/2021/6/2021062549287749.png"/></p><p>当然很多名字起得非常相似，可以自己先添加进去看看，实际确认一下。</p><p><br/></p><p><br/></p><p>最后非常简单的封装了一个C++的类</p><pre class="brush:cpp;toolbar:false">头文件&nbsp;PDHCounterHandler.h

#pragma&nbsp;once

#include&nbsp;&lt;map&gt;
#include&nbsp;&lt;string&gt;
#include&nbsp;&lt;pdh.h&gt;
#include&nbsp;&lt;pdhmsg.h&gt;

#pragma&nbsp;comment(lib,&nbsp;&quot;pdh.lib&quot;)

/*
使用方法
CPDHCounterHandler&nbsp;mPDHCounter;
//1.初始化
mPDHCounter.Init();
//2.添加counter
mPDHCounter.AddCounter(&quot;\\LogicalDisk(F:)\\%&nbsp;Disk&nbsp;Time&quot;,&nbsp;&quot;DiskTimeF&quot;);
//3.循环读取counter的数值
while&nbsp;(true)
{
	//刷新counter数值，两次调用间隔不能太短&nbsp;官方写的是1秒&nbsp;可自由发挥
	Sleep(1000);
	mPDHCounter.CollectData();

	//读取数值
	printf(&quot;F:&nbsp;%d&nbsp;\n&quot;,&nbsp;mPDHCounter.GetCounterValue(&quot;DiskTimeF&quot;,&nbsp;PDH_FMT_LONG).longValue);
}


如何查看各种counter的路径
win+R，打开perfmon.msc（性能监视器）
右键添加计数器即可查看
*/
class&nbsp;CPDHCounterHandler
{
private:
	HQUERY&nbsp;m_hQuery;
	std::map&lt;std::string,&nbsp;HCOUNTER&gt;&nbsp;m_mapCounter;
	
public:
	CPDHCounterHandler();
	~CPDHCounterHandler();

	bool&nbsp;Init();
	bool&nbsp;UnInit();
	bool&nbsp;AddCounter(const&nbsp;std::string&nbsp;&amp;strCounterPath,&nbsp;const&nbsp;std::string&nbsp;&amp;strAlias&nbsp;=&nbsp;&quot;&quot;);
	/*
	刷新数据&nbsp;
	GetCounterValue前一定要先调用此方法刷新数据&nbsp;
	两次调用此函数间隔不要太短
	*/
	bool&nbsp;CollectData();
	bool&nbsp;HasCounter(const&nbsp;std::string&nbsp;&amp;strCounterName);
	PDH_FMT_COUNTERVALUE&nbsp;GetCounterValue(const&nbsp;std::string&nbsp;&amp;strCounterName,&nbsp;int&nbsp;nFmt);

	//void&nbsp;ShowCounterBrowser();
};


Cpp&nbsp;&nbsp;PDHCounterHandler.cpp

#include&nbsp;&quot;stdafx.h&quot;
#include&nbsp;&quot;PDHCounterHandler.h&quot;



CPDHCounterHandler::CPDHCounterHandler()
{
	m_hQuery&nbsp;=&nbsp;NULL;

}


CPDHCounterHandler::~CPDHCounterHandler()
{
	UnInit();
}

bool&nbsp;CPDHCounterHandler::Init()
{
	PDH_STATUS&nbsp;Status;
	Status&nbsp;=&nbsp;PdhOpenQueryA(NULL,&nbsp;NULL,&nbsp;&amp;m_hQuery);
	if&nbsp;(Status&nbsp;!=&nbsp;ERROR_SUCCESS)
	{
		printf(&quot;PdhOpenQuery&nbsp;failed&nbsp;with&nbsp;status&nbsp;0x%x.\n&quot;,&nbsp;Status);
		return&nbsp;false;
	}

	return&nbsp;true;
}

bool&nbsp;CPDHCounterHandler::UnInit()
{
	if&nbsp;(m_hQuery)
	{
		PdhCloseQuery(m_hQuery);
		m_hQuery&nbsp;=&nbsp;NULL;
	}

	return&nbsp;true;
}


bool&nbsp;CPDHCounterHandler::AddCounter(const&nbsp;std::string&nbsp;&amp;strCounterPath,&nbsp;const&nbsp;std::string&nbsp;&amp;strAlias/*&nbsp;=&nbsp;&quot;&quot;*/)
{
	PDH_STATUS&nbsp;Status;
	HCOUNTER&nbsp;hCounter;
	Status&nbsp;=&nbsp;PdhAddCounterA(m_hQuery,&nbsp;strCounterPath.c_str(),&nbsp;0,&nbsp;&amp;hCounter);
	if&nbsp;(Status&nbsp;!=&nbsp;ERROR_SUCCESS)
	{
		printf(&quot;PdhAddCounter&nbsp;%s&nbsp;failed&nbsp;with&nbsp;status&nbsp;0x%x.\n&quot;,&nbsp;strCounterPath.c_str(),&nbsp;Status);
		return&nbsp;false;
	}

	//
	//&nbsp;Most&nbsp;counters&nbsp;require&nbsp;two&nbsp;sample&nbsp;values&nbsp;to&nbsp;display&nbsp;a&nbsp;formatted&nbsp;value.
	//&nbsp;PDH&nbsp;stores&nbsp;the&nbsp;current&nbsp;sample&nbsp;value&nbsp;and&nbsp;the&nbsp;previously&nbsp;collected
	//&nbsp;sample&nbsp;value.&nbsp;This&nbsp;call&nbsp;retrieves&nbsp;the&nbsp;first&nbsp;value&nbsp;that&nbsp;will&nbsp;be&nbsp;used
	//&nbsp;by&nbsp;PdhGetFormattedCounterValue&nbsp;in&nbsp;the&nbsp;first&nbsp;iteration&nbsp;of&nbsp;the&nbsp;loop
	//&nbsp;Note&nbsp;that&nbsp;this&nbsp;value&nbsp;is&nbsp;lost&nbsp;if&nbsp;the&nbsp;counter&nbsp;does&nbsp;not&nbsp;require&nbsp;two
	//&nbsp;values&nbsp;to&nbsp;compute&nbsp;a&nbsp;displayable&nbsp;value.
	//

	//创建后就刷新一下数据
	if&nbsp;(!CollectData())
		return&nbsp;false;

	if&nbsp;(strAlias.empty())
		m_mapCounter.insert(std::make_pair(strCounterPath,&nbsp;hCounter));
	else
		m_mapCounter.insert(std::make_pair(strAlias,&nbsp;hCounter));

	return&nbsp;true;
}

//刷新数据
bool&nbsp;CPDHCounterHandler::CollectData()
{
	PDH_STATUS&nbsp;Status;
	Status&nbsp;=&nbsp;PdhCollectQueryData(m_hQuery);
	if&nbsp;(Status&nbsp;!=&nbsp;ERROR_SUCCESS)
	{
		printf(&quot;PdhCollectQueryData&nbsp;failed&nbsp;with&nbsp;0x%x.\n&quot;,&nbsp;Status);
		return&nbsp;false;
	}

	return&nbsp;true;
}

bool&nbsp;CPDHCounterHandler::HasCounter(const&nbsp;std::string&nbsp;&amp;strCounterName)
{
	auto&nbsp;iter&nbsp;=&nbsp;m_mapCounter.find(strCounterName);
	if&nbsp;(iter&nbsp;==&nbsp;m_mapCounter.end())
		return&nbsp;false;

	return&nbsp;true;
}

/*
fmt:PDH_FMT_DOUBLE&nbsp;PDH_FMT_LONG&nbsp;...
*/
PDH_FMT_COUNTERVALUE&nbsp;CPDHCounterHandler::GetCounterValue(const&nbsp;std::string&nbsp;&amp;strCounterName,&nbsp;int&nbsp;nFmt)
{
	PDH_FMT_COUNTERVALUE&nbsp;mRet&nbsp;=&nbsp;{&nbsp;0&nbsp;};
	auto&nbsp;iter&nbsp;=&nbsp;m_mapCounter.find(strCounterName);
	if&nbsp;(iter&nbsp;==&nbsp;m_mapCounter.end())
		return&nbsp;mRet;

	PDH_STATUS&nbsp;Status;
	DWORD&nbsp;CounterType;
	Status&nbsp;=&nbsp;PdhGetFormattedCounterValue(iter-&gt;second,
		nFmt,
		&amp;CounterType,
		&amp;mRet);
	if&nbsp;(Status&nbsp;!=&nbsp;ERROR_SUCCESS)
	{
		printf(&quot;PdhGetFormattedCounterValue&nbsp;failed&nbsp;with&nbsp;status&nbsp;0x%x.&quot;,&nbsp;Status);
	}

	return&nbsp;mRet;
}
//&nbsp;
//&nbsp;void&nbsp;CPDHCounterHandler::ShowCounterBrowser()
//&nbsp;{
//&nbsp;	//
//&nbsp;	//&nbsp;Initialize&nbsp;the&nbsp;browser&nbsp;dialog&nbsp;window&nbsp;settings.
//&nbsp;	//
//&nbsp;	PDH_STATUS&nbsp;Status;
//&nbsp;	PDH_BROWSE_DLG_CONFIG_A&nbsp;BrowseDlgData;
//&nbsp;	char&nbsp;CounterPathBuffer[PDH_MAX_COUNTER_PATH];
//&nbsp;
//&nbsp;	ZeroMemory(&amp;CounterPathBuffer,&nbsp;sizeof(CounterPathBuffer));
//&nbsp;	ZeroMemory(&amp;BrowseDlgData,&nbsp;sizeof(PDH_BROWSE_DLG_CONFIG));
//&nbsp;
//&nbsp;	BrowseDlgData.bIncludeInstanceIndex&nbsp;=&nbsp;FALSE;
//&nbsp;	BrowseDlgData.bSingleCounterPerAdd&nbsp;=&nbsp;TRUE;
//&nbsp;	BrowseDlgData.bSingleCounterPerDialog&nbsp;=&nbsp;TRUE;
//&nbsp;	BrowseDlgData.bLocalCountersOnly&nbsp;=&nbsp;FALSE;
//&nbsp;	BrowseDlgData.bWildCardInstances&nbsp;=&nbsp;TRUE;
//&nbsp;	BrowseDlgData.bHideDetailBox&nbsp;=&nbsp;TRUE;
//&nbsp;	BrowseDlgData.bInitializePath&nbsp;=&nbsp;FALSE;
//&nbsp;	BrowseDlgData.bDisableMachineSelection&nbsp;=&nbsp;FALSE;
//&nbsp;	BrowseDlgData.bIncludeCostlyObjects&nbsp;=&nbsp;FALSE;
//&nbsp;	BrowseDlgData.bShowObjectBrowser&nbsp;=&nbsp;FALSE;
//&nbsp;	BrowseDlgData.hWndOwner&nbsp;=&nbsp;NULL;
//&nbsp;	BrowseDlgData.szReturnPathBuffer&nbsp;=&nbsp;CounterPathBuffer;
//&nbsp;	BrowseDlgData.cchReturnPathLength&nbsp;=&nbsp;PDH_MAX_COUNTER_PATH;
//&nbsp;	BrowseDlgData.pCallBack&nbsp;=&nbsp;NULL;
//&nbsp;	BrowseDlgData.dwCallBackArg&nbsp;=&nbsp;0;
//&nbsp;	BrowseDlgData.CallBackStatus&nbsp;=&nbsp;ERROR_SUCCESS;
//&nbsp;	BrowseDlgData.dwDefaultDetailLevel&nbsp;=&nbsp;PERF_DETAIL_WIZARD;
//&nbsp;	BrowseDlgData.szDialogBoxCaption&nbsp;=&nbsp;&quot;Select&nbsp;a&nbsp;counter&nbsp;to&nbsp;monitor.&quot;;
//&nbsp;
//&nbsp;	//
//&nbsp;	//&nbsp;Display&nbsp;the&nbsp;counter&nbsp;browser&nbsp;window.&nbsp;The&nbsp;dialog&nbsp;is&nbsp;configured
//&nbsp;	//&nbsp;to&nbsp;return&nbsp;a&nbsp;single&nbsp;selection&nbsp;from&nbsp;the&nbsp;counter&nbsp;list.
//&nbsp;	//
//&nbsp;
//&nbsp;	Status&nbsp;=&nbsp;PdhBrowseCountersA(&amp;BrowseDlgData);
//&nbsp;
//&nbsp;	if&nbsp;(Status&nbsp;!=&nbsp;ERROR_SUCCESS)
//&nbsp;	{
//&nbsp;		if&nbsp;(Status&nbsp;==&nbsp;PDH_DIALOG_CANCELLED)
//&nbsp;		{
//&nbsp;			printf(&quot;Dialog&nbsp;canceled&nbsp;by&nbsp;user.\n&quot;);
//&nbsp;		}
//&nbsp;		else
//&nbsp;		{
//&nbsp;			printf(&quot;PdhBrowseCounters&nbsp;failed&nbsp;with&nbsp;status&nbsp;0x%x.\n&quot;,&nbsp;Status);
//&nbsp;		}
//&nbsp;	}
//&nbsp;	else&nbsp;if&nbsp;(strlen(CounterPathBuffer)&nbsp;==&nbsp;0)
//&nbsp;	{
//&nbsp;		printf(&quot;User&nbsp;did&nbsp;not&nbsp;select&nbsp;any&nbsp;counter.&nbsp;\n&quot;);
//&nbsp;	}
//&nbsp;	else
//&nbsp;	{
//&nbsp;		printf(&quot;Counter&nbsp;selected:&nbsp;%ls&nbsp;\n&quot;,&nbsp;CounterPathBuffer);
//&nbsp;	}
//}</pre><p><br/></p><p><br/></p><p>最后贴一个示例：</p><pre class="brush:cpp;toolbar:false">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CPDHCounterHandler&nbsp;mPDHCounter;
	mPDHCounter.Init();
	mPDHCounter.AddCounter(&quot;\\LogicalDisk(F:)\\%&nbsp;Disk&nbsp;Time&quot;,&nbsp;&quot;DiskTimeF&quot;);
	while&nbsp;(true)
	{
		//刷新监控器
		Sleep(1000);
		mPDHCounter.CollectData();

		printf(&quot;F:&nbsp;%d&nbsp;\n&quot;,&nbsp;mPDHCounter.GetCounterValue(&quot;DiskTimeF&quot;,&nbsp;PDH_FMT_LONG).longValue);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//只要init之后，可以随时添加Counter进去
		if&nbsp;(!mPDHCounter.HasCounter(&quot;DiskTimeE&quot;))
		{
			mPDHCounter.AddCounter(&quot;\\LogicalDisk(E:)\\%&nbsp;Disk&nbsp;Time&quot;,&nbsp;&quot;DiskTimeE&quot;);
		}
		else
		{
			printf(&quot;E:&nbsp;%d&nbsp;\n&quot;,&nbsp;mPDHCounter.GetCounterValue(&quot;DiskTimeE&quot;,&nbsp;PDH_FMT_LONG).longValue);

			if&nbsp;(!mPDHCounter.HasCounter(&quot;DiskTimeD&quot;))
			{
				mPDHCounter.AddCounter(&quot;\\LogicalDisk(D:)\\%&nbsp;Disk&nbsp;Time&quot;,&nbsp;&quot;DiskTimeD&quot;);
			}
			else
			{
				printf(&quot;D:&nbsp;%d&nbsp;\n&quot;,&nbsp;mPDHCounter.GetCounterValue(&quot;DiskTimeD&quot;,&nbsp;PDH_FMT_LONG).longValue);
			}
		}
	}</pre><p><br/></p><p><img src="http://www.fenlog.com/zb_users/upload/2021/6/2021062549580649.png"/></p>]]></description><category>C/C++代码</category><comments>http://www.fenlog.com/post/127.html#comment</comments><wfw:commentRss>http://www.fenlog.com/feed.asp?cmt=127</wfw:commentRss></item><item><title>mongoose魔改历程(C++封装http和ws服务、多线程、优化)-2024.12.16更新</title><author>345382462@qq.com (blackfeather)</author><link>http://www.fenlog.com/post/126.html</link><pubDate>Fri, 30 Oct 2020 19:42:05 +0800</pubDate><guid>http://www.fenlog.com/post/126.html</guid><description><![CDATA[<p><br/></p><p>2024.12.16更新</p><p>评论提到的ws问题修正了，实际上就是注释掉了一个多余的判断处理。</p><p>mongoose版本是6.18版本</p><p><a href="https://github.com/cesanta/mongoose/releases/tag/6.18" _src="https://github.com/cesanta/mongoose/releases/tag/6.18">https://github.com/cesanta/mongoose/releases/tag/6.18</a> </p><p>题外话：</p><p>当时写这个cpp用了两天，用了一年多后来平台升级切换到了C++17就用了其他框架。后来发现虽然过去了好几年，但是关注的人还是很多，说明<span style="text-wrap-mode: wrap;">mongoose真的是好使，虽然有很多性能更好的库，但是在嵌入式设备等某些场景真的很实用，这也是当时选这个库作为框架的原因</span>。这里面有个小BUG，所以代码更新修正了，有问题欢迎留言或者加QQ沟通继续完善。不提github了，某些原因不想提交上去。。。下面也推荐了几个笔者最近用过的其他框架，也很nice。</p><p><br/></p><p><br/></p><p>2024.5.22更新</p><p>推荐几个已经使用的http库：</p><p><span style="color: rgb(31, 35, 40); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, &quot;Noto Sans&quot;, Helvetica, Arial, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;; font-size: 16px; text-wrap: wrap; background-color: rgb(255, 255, 255);">A C++11 single-file header-only cross platform HTTP/HTTPS library.</span></p><p><a href="https://github.com/yhirose/cpp-httplib" _src="https://github.com/yhirose/cpp-httplib">https://github.com/yhirose/cpp-httplib</a></p><p>跟mongoose类似的框架，单文件无依赖跨平台，include后就能使用，这个库的作者也很积极，反馈过BUG提交过PR都处理的非常好，性能不要求很变态的情况下很推荐！</p><p><span style="color: rgb(31, 35, 40); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, &quot;Noto Sans&quot;, Helvetica, Arial, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;; font-size: 16px; text-wrap: wrap; background-color: rgb(255, 255, 255);"><span style="text-wrap-mode: wrap;">国产高性能框架</span>基于C++17/20的Http应用框架，使用Drogon可以方便的使用C++构建各种类型的Web应用服务端程序</span></p><p><a href="https://github.com/drogonframework/drogon" _src="https://github.com/drogonframework/drogon">https://github.com/drogonframework/drogon</a></p><p><span style="color: rgb(31, 35, 40); font-family: -apple-system, BlinkMacSystemFont, &quot;Segoe UI&quot;, &quot;Noto Sans&quot;, Helvetica, Arial, sans-serif, &quot;Apple Color Emoji&quot;, &quot;Segoe UI Emoji&quot;; font-size: 16px; text-wrap: wrap; background-color: rgb(255, 255, 255);">Header only c++ network library, based on asio,support tcp,udp,http,websocket,rpc,ssl,icmp,serial_port.</span></p><p><a href="https://github.com/zhllxt/asio2" _src="https://github.com/zhllxt/asio2">https://github.com/zhllxt/asio2</a> </p><p><br/></p><p><br/></p><p>背景需求</p><p>每个工作背景都不同，需要总结一下：</p><p>1.C++编写，跨平台（windows上用的vs2013，所以是C++11标准）</p><p>2.支持http和ws的server，支持多线程，支持uri映射(类似addhandler(&quot;/hello&quot;,onrequestcallback);)，支持请求中相关字段的获取（get/post参数读取、header、cookie等读取）</p><p>3.不用性能超强，并发数也不大（几十并发）</p><p>4.依赖库少一点，不想为了一个小功能引入boost之类的大家伙</p><p>之前一直用的是libevent库提供的http服务作为一个接口服务器，后来有了新的需求要增加ws支持，官方的不支持ws，自己魔改libevent增加ws支持实力不够改的兼容性不好，框架网上有大把的框架，用现成的吧。</p><p><br/></p><p>&nbsp;</p><p><br/></p><p>筛选</p><p>查找了很多库 https://github.com/fffaraz/awesome-cpp#networking</p><p>前后测试了两遍没有完全满足的，都要二次封装，那么就选一个库作为基石来鼓捣。</p><p>最后选择的是mongoose（具体过程不说了，完全是个人情况而定的）</p><p>mongoose一个很单纯的C编写的库，包含了client和server的功能，单线程业务处理，不过提供的方法倒是挺多的，非常方便使用。不过这个库不是性能优先，如果非常注重性能（几万并发起步）那还是不要用了，不过却满足我的当前需求。</p><p><br/></p><p>&nbsp;</p><p><br/></p><p>建立http服务</p><p>mongoose简单示例（官方）</p><pre class="brush:cpp;toolbar:false">#include&nbsp;&quot;mongoose.h&quot;

//连接上所有事件的回调函数
static&nbsp;void&nbsp;ev_handler(struct&nbsp;mg_connection&nbsp;*nc,&nbsp;int&nbsp;ev,&nbsp;void&nbsp;*p)&nbsp;
{
&nbsp;&nbsp;
&nbsp;&nbsp;//http请求
&nbsp;&nbsp;if&nbsp;(ev&nbsp;==&nbsp;MG_EV_HTTP_REQUEST)&nbsp;
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;//发送返回值，有多个函数可以用不同姿势发送返回值
&nbsp;&nbsp;&nbsp;&nbsp;mg_printf(nc,&nbsp;&quot;%s&quot;,&nbsp;&quot;HTTP/1.1&nbsp;200&nbsp;OK\r\nContent-Length:&nbsp;2\r\n\r\nOK&quot;);
&nbsp;&nbsp;}

}

int&nbsp;main(void)&nbsp;
{
&nbsp;&nbsp;struct&nbsp;mg_mgr&nbsp;mgr;
&nbsp;&nbsp;struct&nbsp;mg_connection&nbsp;*nc;

&nbsp;&nbsp;mg_mgr_init(&amp;mgr,&nbsp;NULL);&nbsp;//初始化连接管理器

&nbsp;&nbsp;//绑定端口，设置回调函数
&nbsp;&nbsp;nc&nbsp;=&nbsp;mg_bind(&amp;mgr,&nbsp;&quot;8000&quot;,&nbsp;ev_handler);&nbsp;
&nbsp;&nbsp;if&nbsp;(nc&nbsp;==&nbsp;NULL)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;printf(&quot;Failed&nbsp;to&nbsp;create&nbsp;listener\n&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;1;
&nbsp;&nbsp;}

&nbsp;&nbsp;//允许http和websocket
&nbsp;&nbsp;mg_set_protocol_http_websocket(nc);&nbsp;&nbsp;

&nbsp;&nbsp;//事件循环
&nbsp;&nbsp;for&nbsp;(;;)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;mg_mgr_poll(&amp;mgr,&nbsp;1000);
&nbsp;&nbsp;}

&nbsp;&nbsp;//释放连接管理器
&nbsp;&nbsp;mg_mgr_free(&amp;mgr);

&nbsp;&nbsp;return&nbsp;0;
}</pre><p><br/></p><p>先建立连接管理器(mg_mgr)，然后所有的连接都在mg_mgr中以链表的形式管理。</p><p>然后在mg_mgr中bind一个本地端口，并设置该连接允许http和websocket（mg_set_protocol_http_websocket），然后处理回调消息就完事了，非常简单。</p><p>那么也简单了，按照这几个方法，很容易封装出来一个C++的版本，这里不多贴代码了。</p><p><br/></p><p>&nbsp;</p><p><br/></p><p><br/></p><p>多线程的使用</p><p>多线程的官方示例：</p><pre class="brush:cpp;toolbar:false">#include&nbsp;&quot;mongoose.h&quot;

static&nbsp;sig_atomic_t&nbsp;s_received_signal&nbsp;=&nbsp;0;
static&nbsp;const&nbsp;char&nbsp;*s_http_port&nbsp;=&nbsp;&quot;8000&quot;;
static&nbsp;const&nbsp;int&nbsp;s_num_worker_threads&nbsp;=&nbsp;5;
static&nbsp;unsigned&nbsp;long&nbsp;s_next_id&nbsp;=&nbsp;0;

static&nbsp;void&nbsp;signal_handler(int&nbsp;sig_num)&nbsp;{
&nbsp;&nbsp;signal(sig_num,&nbsp;signal_handler);
&nbsp;&nbsp;s_received_signal&nbsp;=&nbsp;sig_num;
}
static&nbsp;struct&nbsp;mg_serve_http_opts&nbsp;s_http_server_opts;
static&nbsp;sock_t&nbsp;sock[2];

//&nbsp;This&nbsp;info&nbsp;is&nbsp;passed&nbsp;to&nbsp;the&nbsp;worker&nbsp;thread
struct&nbsp;work_request&nbsp;{
&nbsp;&nbsp;unsigned&nbsp;long&nbsp;conn_id;&nbsp;&nbsp;//&nbsp;needed&nbsp;to&nbsp;identify&nbsp;the&nbsp;connection&nbsp;where&nbsp;to&nbsp;send&nbsp;the&nbsp;reply
&nbsp;&nbsp;//&nbsp;optionally,&nbsp;more&nbsp;data&nbsp;that&nbsp;could&nbsp;be&nbsp;required&nbsp;by&nbsp;worker&nbsp;
};

//&nbsp;This&nbsp;info&nbsp;is&nbsp;passed&nbsp;by&nbsp;the&nbsp;worker&nbsp;thread&nbsp;to&nbsp;mg_broadcast
struct&nbsp;work_result&nbsp;{
&nbsp;&nbsp;unsigned&nbsp;long&nbsp;conn_id;
&nbsp;&nbsp;int&nbsp;sleep_time;
};

static&nbsp;void&nbsp;on_work_complete(struct&nbsp;mg_connection&nbsp;*nc,&nbsp;int&nbsp;ev,&nbsp;void&nbsp;*ev_data)&nbsp;{
&nbsp;&nbsp;(void)&nbsp;ev;
&nbsp;&nbsp;char&nbsp;s[32];
&nbsp;&nbsp;struct&nbsp;mg_connection&nbsp;*c;
&nbsp;&nbsp;for&nbsp;(c&nbsp;=&nbsp;mg_next(nc-&gt;mgr,&nbsp;NULL);&nbsp;c&nbsp;!=&nbsp;NULL;&nbsp;c&nbsp;=&nbsp;mg_next(nc-&gt;mgr,&nbsp;c))&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(c-&gt;user_data&nbsp;!=&nbsp;NULL)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;work_result&nbsp;*res&nbsp;=&nbsp;(struct&nbsp;work_result&nbsp;*)ev_data;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;((unsigned&nbsp;long)c-&gt;user_data&nbsp;==&nbsp;res-&gt;conn_id)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sprintf(s,&nbsp;&quot;conn_id:%lu&nbsp;sleep:%d&quot;,&nbsp;res-&gt;conn_id,&nbsp;res-&gt;sleep_time);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mg_send_head(c,&nbsp;200,&nbsp;strlen(s),&nbsp;&quot;Content-Type:&nbsp;text/plain&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mg_printf(c,&nbsp;&quot;%s&quot;,&nbsp;s);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
}

void&nbsp;*worker_thread_proc(void&nbsp;*param)&nbsp;{
&nbsp;&nbsp;struct&nbsp;mg_mgr&nbsp;*mgr&nbsp;=&nbsp;(struct&nbsp;mg_mgr&nbsp;*)&nbsp;param;
&nbsp;&nbsp;struct&nbsp;work_request&nbsp;req&nbsp;=&nbsp;{0};
&nbsp;&nbsp;
&nbsp;&nbsp;while&nbsp;(s_received_signal&nbsp;==&nbsp;0)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(read(sock[1],&nbsp;&amp;req,&nbsp;sizeof(req))&nbsp;&lt;&nbsp;0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(&quot;Reading&nbsp;worker&nbsp;sock&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;r&nbsp;=&nbsp;rand()&nbsp;%&nbsp;10;
&nbsp;&nbsp;&nbsp;&nbsp;sleep(r);
&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;work_result&nbsp;res&nbsp;=&nbsp;{req.conn_id,&nbsp;r};
&nbsp;&nbsp;&nbsp;&nbsp;mg_broadcast(mgr,&nbsp;on_work_complete,&nbsp;(void&nbsp;*)&amp;res,&nbsp;sizeof(res));
&nbsp;&nbsp;}
&nbsp;&nbsp;return&nbsp;NULL;
}

static&nbsp;void&nbsp;ev_handler(struct&nbsp;mg_connection&nbsp;*nc,&nbsp;int&nbsp;ev,&nbsp;void&nbsp;*ev_data)&nbsp;{
&nbsp;&nbsp;(void)&nbsp;nc;
&nbsp;&nbsp;(void)&nbsp;ev_data;
&nbsp;&nbsp;
&nbsp;&nbsp;switch&nbsp;(ev)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;MG_EV_ACCEPT:
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nc-&gt;user_data&nbsp;=&nbsp;(void&nbsp;*)++s_next_id;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;
&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;MG_EV_HTTP_REQUEST:&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;work_request&nbsp;req&nbsp;=&nbsp;{(unsigned&nbsp;long)nc-&gt;user_data};

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(write(sock[0],&nbsp;&amp;req,&nbsp;sizeof(req))&nbsp;&lt;&nbsp;0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;perror(&quot;Writing&nbsp;worker&nbsp;sock&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;MG_EV_CLOSE:&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(nc-&gt;user_data)&nbsp;nc-&gt;user_data&nbsp;=&nbsp;NULL;
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
}

int&nbsp;main(void)&nbsp;{
&nbsp;&nbsp;struct&nbsp;mg_mgr&nbsp;mgr;
&nbsp;&nbsp;struct&nbsp;mg_connection&nbsp;*nc;
&nbsp;&nbsp;int&nbsp;i;

&nbsp;&nbsp;if&nbsp;(mg_socketpair(sock,&nbsp;SOCK_STREAM)&nbsp;==&nbsp;0)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;perror(&quot;Opening&nbsp;socket&nbsp;pair&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;exit(1);
&nbsp;&nbsp;}

&nbsp;&nbsp;signal(SIGTERM,&nbsp;signal_handler);
&nbsp;&nbsp;signal(SIGINT,&nbsp;signal_handler);

&nbsp;&nbsp;mg_mgr_init(&amp;mgr,&nbsp;NULL);

&nbsp;&nbsp;nc&nbsp;=&nbsp;mg_bind(&amp;mgr,&nbsp;s_http_port,&nbsp;ev_handler);
&nbsp;&nbsp;if&nbsp;(nc&nbsp;==&nbsp;NULL)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;printf(&quot;Failed&nbsp;to&nbsp;create&nbsp;listener\n&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;1;
&nbsp;&nbsp;}

&nbsp;&nbsp;mg_set_protocol_http_websocket(nc);
&nbsp;&nbsp;s_http_server_opts.document_root&nbsp;=&nbsp;&quot;.&quot;;&nbsp;&nbsp;//&nbsp;Serve&nbsp;current&nbsp;directory
&nbsp;&nbsp;s_http_server_opts.enable_directory_listing&nbsp;=&nbsp;&quot;no&quot;;

&nbsp;&nbsp;for&nbsp;(i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;s_num_worker_threads;&nbsp;i++)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;mg_start_thread(worker_thread_proc,&nbsp;&amp;mgr);
&nbsp;&nbsp;}

&nbsp;&nbsp;printf(&quot;Started&nbsp;on&nbsp;port&nbsp;%s\n&quot;,&nbsp;s_http_port);
&nbsp;&nbsp;while&nbsp;(s_received_signal&nbsp;==&nbsp;0)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;mg_mgr_poll(&amp;mgr,&nbsp;200);
&nbsp;&nbsp;}

&nbsp;&nbsp;mg_mgr_free(&amp;mgr);

&nbsp;&nbsp;closesocket(sock[0]);
&nbsp;&nbsp;closesocket(sock[1]);

&nbsp;&nbsp;return&nbsp;0;
}</pre><p>开始看的我有点懵逼，建立了一个mg_socketpair来完成业务线程与工作线程的通信，在多线程的工作线程中要用mg_broadcast回调在回调里面真正发送数据，开始没有很好地理解写错了，崩溃了整整一宿（20:00到3:00）。后来仔细阅读了代码结合一些其他人的描述，才弄明白原理。</p><p>首先mongoose是业务线程是单线程，就是mg_mgr_poll那个循环就是业务线程，也就是说ev_handler这个回调和发送返回值操作都要在业务线程中做，不在业务线程中调用发送函数(例如mg_send_head)是错误的直接蹦。工作线程是可以多个线程来处理业务，处理完毕后要通知回业务线程。这个通知的方法就是mg_broadcast。</p><p>至于这个mg_socketpair可以认为是一个队列，用于业务线程接收到事件后，封装数据的发送到工作线程。补个图。</p><p><img src="http://www.fenlog.com/zb_users/upload/2020/10/2020103071083161.jpg" width="1440" height="1080" title="微信图片_20201030171508.jpg" alt="微信图片_20201030171508.jpg"/></p><p><br/></p><p><br/></p><p>&nbsp;</p><p><br/></p><p>多线程之间通信</p><p><br/></p><p>ev_handler回调的数据发送到工作线程</p><p>官方示例中使用mg_socketpair通信，其实就是建立了一对socket一个作为发送者一个作为接收者，就是socket通信而已。实际上可以用队列比较方便。建议使用无锁队列效率比较高（写的第一版用的是加锁队列，效率差了十多倍），无锁队列库：https://github.com/cameron314/concurrentqueue&nbsp; （个人比较喜欢的header only方式）</p><p>数据传输这里，开始我直接传递的event_data指针，发现完全不行，因为ev_handler返回后该数据会被销毁，所以要在回调中把event_data中所需要的数据都缓存下来。比如http的回调事件MG_EV_HTTP_REQUEST，event_data是http_message*数据，里面包含uri、mehtod、body、message、header等等信息，都要自己另存下来，否则回调一返回数据就会被销毁了。保存好这份数据后，将其插入队列等待工作线程取数据处理。这时候回调返回，业务线程可以继续loop去干其他事情。</p><pre class="brush:cpp;toolbar:false">			http_message&nbsp;*msg&nbsp;=&nbsp;(http_message&nbsp;*)event_data;

			//保存回调里面的关键数据
			MG_EVENT_DATA_PTR&nbsp;cbdata&nbsp;=&nbsp;new&nbsp;MG_EVENT_DATA;
			cbdata-&gt;event_type&nbsp;=&nbsp;event_type;
			cbdata-&gt;body.assign(msg-&gt;body.p,&nbsp;msg-&gt;body.len);
			cbdata-&gt;method.assign(msg-&gt;method.p,&nbsp;msg-&gt;method.len);
			cbdata-&gt;uri.assign(msg-&gt;uri.p,&nbsp;msg-&gt;uri.len);
			cbdata-&gt;query.assign(msg-&gt;query_string.p,&nbsp;msg-&gt;query_string.len);
			cbdata-&gt;proto.assign(msg-&gt;proto.p,&nbsp;msg-&gt;proto.len);
			cbdata-&gt;contentlength&nbsp;=&nbsp;msg-&gt;content_length;
			for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;MG_MAX_HTTP_HEADERS;&nbsp;i++)
			{
				if&nbsp;(msg-&gt;header_names[i].len&nbsp;==&nbsp;0)
					break;
				cbdata-&gt;headers.emplace_back(std::string(msg-&gt;header_names[i].p,&nbsp;msg-&gt;header_names[i].len),&nbsp;std::string(msg-&gt;header_values[i].p,&nbsp;msg-&gt;header_values[i].len));
			}
			//将cbdata插入队列</pre><p><br/></p><p>工作线程处理与返回</p><p><br/></p><p>工作线程是多线程，例如4线程就用std::thread开启4个work_handler()。里面的逻辑就是先从队列里面取出来数据，然后取出来数据后回调给用户接口，用户接口干活，干完之后封装返回值，这些都不用多说就是个简单的callback而已。这里再次强调不能直接在工作线程中调用发送方法（例如mg_send_head），要用回调将数据返回给业务线程去处理。重点就是mg_broadcast这个坑，写的真尼玛晦涩难懂，还都是坑，怪不得网上很多人都说官方的多线程例子是坨屎。</p><p>首先说说mg_broadcast这个方法</p><pre class="brush:cpp;toolbar:false">//工作线程中调用
void&nbsp;mg_broadcast(struct&nbsp;mg_mgr&nbsp;*mgr,&nbsp;mg_event_handler_t&nbsp;cb,&nbsp;void&nbsp;*data,&nbsp;size_t&nbsp;len)&nbsp;{
&nbsp;&nbsp;struct&nbsp;ctl_msg&nbsp;ctl_msg;

&nbsp;&nbsp;/*
&nbsp;&nbsp;&nbsp;*&nbsp;Mongoose&nbsp;manager&nbsp;has&nbsp;a&nbsp;socketpair,&nbsp;`struct&nbsp;mg_mgr::ctl`,
&nbsp;&nbsp;&nbsp;*&nbsp;where&nbsp;`mg_broadcast()`&nbsp;pushes&nbsp;the&nbsp;message.
&nbsp;&nbsp;&nbsp;*&nbsp;`mg_mgr_poll()`&nbsp;wakes&nbsp;up,&nbsp;reads&nbsp;a&nbsp;message&nbsp;from&nbsp;the&nbsp;socket&nbsp;pair,&nbsp;and&nbsp;calls
&nbsp;&nbsp;&nbsp;*&nbsp;specified&nbsp;callback&nbsp;for&nbsp;each&nbsp;connection.&nbsp;Thus&nbsp;the&nbsp;callback&nbsp;function&nbsp;executes
&nbsp;&nbsp;&nbsp;*&nbsp;in&nbsp;event&nbsp;manager&nbsp;thread.
&nbsp;&nbsp;&nbsp;*/
&nbsp;&nbsp;if&nbsp;(mgr-&gt;ctl[0]&nbsp;!=&nbsp;INVALID_SOCKET&nbsp;&amp;&amp;&nbsp;data&nbsp;!=&nbsp;NULL&nbsp;&amp;&amp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;len&nbsp;&lt;&nbsp;sizeof(ctl_msg.message))&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;dummy;

&nbsp;&nbsp;&nbsp;&nbsp;ctl_msg.callback&nbsp;=&nbsp;cb;
&nbsp;&nbsp;&nbsp;&nbsp;memcpy(ctl_msg.message,&nbsp;data,&nbsp;len);
&nbsp;&nbsp;&nbsp;&nbsp;dummy&nbsp;=&nbsp;MG_SEND_FUNC(mgr-&gt;ctl[0],&nbsp;(char&nbsp;*)&nbsp;&amp;ctl_msg,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;offsetof(struct&nbsp;ctl_msg,&nbsp;message)&nbsp;+&nbsp;len,&nbsp;0);
&nbsp;&nbsp;&nbsp;&nbsp;dummy&nbsp;=&nbsp;MG_RECV_FUNC(mgr-&gt;ctl[0],&nbsp;(char&nbsp;*)&nbsp;&amp;len,&nbsp;1,&nbsp;0);
&nbsp;&nbsp;&nbsp;&nbsp;(void)&nbsp;dummy;&nbsp;/*&nbsp;https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509&nbsp;*/
&nbsp;&nbsp;}
}


//业务线程中接收
static&nbsp;void&nbsp;mg_mgr_handle_ctl_sock(struct&nbsp;mg_mgr&nbsp;*mgr)&nbsp;{
&nbsp;&nbsp;struct&nbsp;ctl_msg&nbsp;ctl_msg;
&nbsp;&nbsp;int&nbsp;len&nbsp;=&nbsp;(int)&nbsp;MG_RECV_FUNC(mgr-&gt;ctl[1],&nbsp;(char&nbsp;*)&nbsp;&amp;ctl_msg,&nbsp;sizeof(ctl_msg),&nbsp;0);
&nbsp;&nbsp;size_t&nbsp;dummy&nbsp;=&nbsp;MG_SEND_FUNC(mgr-&gt;ctl[1],&nbsp;ctl_msg.message,&nbsp;1,&nbsp;0);
&nbsp;&nbsp;DBG((&quot;read&nbsp;%d&nbsp;from&nbsp;ctl&nbsp;socket&quot;,&nbsp;len));
&nbsp;&nbsp;(void)&nbsp;dummy;&nbsp;/*&nbsp;https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509&nbsp;*/
&nbsp;&nbsp;if&nbsp;(len&nbsp;&gt;=&nbsp;(int)&nbsp;sizeof(ctl_msg.callback)&nbsp;&amp;&amp;&nbsp;ctl_msg.callback&nbsp;!=&nbsp;NULL)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;mg_connection&nbsp;*nc;
&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(nc&nbsp;=&nbsp;mg_next(mgr,&nbsp;NULL);&nbsp;nc&nbsp;!=&nbsp;NULL;&nbsp;nc&nbsp;=&nbsp;mg_next(mgr,&nbsp;nc))&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//回调方法
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctl_msg.callback(nc,&nbsp;MG_EV_POLL,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ctl_msg.message&nbsp;MG_UD_ARG(nc-&gt;user_data));
&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
}</pre><p><br/></p><p>void mg_broadcast(struct mg_mgr *mgr, mg_event_handler_t cb, void *data, size_t len);</p><p>第二个参数是广播的回调方法，data和len是数据。工作线程中调用mg_broadcast来广播，触发的回调方法里面就是业务线程了（开始没理解，后来通过打印调用和回调的threadid才明白）。</p><p>内部很简单其实就是一个在内部建立了一个mg_socketpair（上面描述过），将要传递的数据封装到结构体里面，工作线程中调用send把结构体发送出去，业务线程里面recv到这个结构体，然后触发回调，很简单的逻辑。这里官方代码很坑，connection在连接后 （ MG_EV_ACCEPT 事件），初始化了一个唯一connid（ s_next_id ）给每个connection（存储到了user_data里面），广播会将每个connection都回调一遍(上面代码里面nc那个链表的遍历)，然后在回调里面判断当前的connection是不是我要发送数据的connid，不是的话返回，是的话继续发送数据。官方示例里面在on_work_complete又遍历了一遍所有connection这里其实是错误的，一个大坑，因为回调已经是遍历所有connection回调了。这个也符合mg_broadcast这个名字，广播给所有connection回调。</p><p><br/></p><p>这里还有一个大坑，因为mg_broadcast其实也是异步的，利用socket发送用户数据，那个结构体的定义为：</p><p>struct ctl_msg {&nbsp;</p><p>&nbsp; &nbsp; mg_event_handler_t callback;&nbsp;</p><p>&nbsp; &nbsp; char message[MG_CTL_MSG_MESSAGE_SIZE];&nbsp; //默认是8192&nbsp;</p><p>};&nbsp;</p><p>所以这里传递的数据不能是原始返回内容的buffer，太小了不合适呀，所以要new一个buffer传递进去，然后在回调里面去delete掉。但这里会引入另外一个问题，broadcast里面是遍历所有正常的connection，如果某个用户接口处理时间比较长比如要2秒，返回的时候connection已经断开了（多线程下是可能存在的），那么这个广播后就不能触发回调，导致这份buffer无法被删除造成内存泄露。</p><p><br/></p><p>&nbsp;</p><p><br/></p><p>问题总结：</p><p>1.广播时connection断开了无法删除buffer</p><p>2.异步的，不知道当前connection的状态</p><p>3.工作线程与业务线程传递数据使用广播，意味着每次都要通知一遍所有的connection，这完全没有必要呀，我有connid那只回调一个connection就可以了哇。当然原本的broadcast到所有nc还是有必要的。</p><p><br/></p><p><br/></p><p>开始魔改mg_broadcast吧。。。改后的代码：</p><pre class="brush:cpp;toolbar:false">struct&nbsp;ctl_msg&nbsp;{
&nbsp;&nbsp;mg_event_handler_t&nbsp;callback;
&nbsp;&nbsp;uint32_t&nbsp;connid;&nbsp;&nbsp;//connID，可以遍历指定的connection
&nbsp;&nbsp;int&nbsp;syncflag;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//异步和同步执行标记
&nbsp;&nbsp;void&nbsp;*data;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//传递的数据
};

//增加connid参数，可以指定connid来触发回调，如果connid为0那就是广播（给connection初始化connid的时候不能设置为0）
//data：&nbsp;用户数据指针
//ret：返回值指针，NULL为异步，如果设置了就意味着同步发送&nbsp;发送完毕后才返回。同步方式可以节省内存，异步发送需要新开辟内存
void&nbsp;mg_broadcast(struct&nbsp;mg_mgr&nbsp;*mgr,&nbsp;uint32_t&nbsp;connid,&nbsp;mg_event_handler_t&nbsp;cb,&nbsp;void&nbsp;*data,&nbsp;int&nbsp;*ret)&nbsp;{
&nbsp;&nbsp;struct&nbsp;ctl_msg&nbsp;ctl_msg;
&nbsp;&nbsp;/*
&nbsp;&nbsp;&nbsp;*&nbsp;Mongoose&nbsp;manager&nbsp;has&nbsp;a&nbsp;socketpair,&nbsp;`struct&nbsp;mg_mgr::ctl`,
&nbsp;&nbsp;&nbsp;*&nbsp;where&nbsp;`mg_broadcast()`&nbsp;pushes&nbsp;the&nbsp;message.
&nbsp;&nbsp;&nbsp;*&nbsp;`mg_mgr_poll()`&nbsp;wakes&nbsp;up,&nbsp;reads&nbsp;a&nbsp;message&nbsp;from&nbsp;the&nbsp;socket&nbsp;pair,&nbsp;and&nbsp;calls
&nbsp;&nbsp;&nbsp;*&nbsp;specified&nbsp;callback&nbsp;for&nbsp;each&nbsp;connection.&nbsp;Thus&nbsp;the&nbsp;callback&nbsp;function&nbsp;executes
&nbsp;&nbsp;&nbsp;*&nbsp;in&nbsp;event&nbsp;manager&nbsp;thread.
&nbsp;&nbsp;&nbsp;*/
&nbsp;&nbsp;if&nbsp;(mgr-&gt;ctl[0]&nbsp;!=&nbsp;INVALID_SOCKET&nbsp;&amp;&amp;&nbsp;data&nbsp;!=&nbsp;NULL)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;size_t&nbsp;dummy;

&nbsp;&nbsp;&nbsp;&nbsp;ctl_msg.callback&nbsp;=&nbsp;cb;
	ctl_msg.connid&nbsp;=&nbsp;connid;
	if&nbsp;(ret)
		ctl_msg.syncflag&nbsp;=&nbsp;1;
	ctl_msg.data&nbsp;=&nbsp;data;
	dummy&nbsp;=&nbsp;MG_SEND_FUNC(mgr-&gt;ctl[0],&nbsp;(char&nbsp;*)&amp;ctl_msg,&nbsp;sizeof(ctl_msg),&nbsp;0);
	dummy&nbsp;=&nbsp;MG_RECV_FUNC(mgr-&gt;ctl[0],&nbsp;(char&nbsp;*)&amp;ctl_msg.syncflag,&nbsp;sizeof(int),&nbsp;0);
&nbsp;&nbsp;&nbsp;&nbsp;(void)&nbsp;dummy;&nbsp;/*&nbsp;https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509&nbsp;*/
	if&nbsp;(ret)
		*ret&nbsp;=&nbsp;ctl_msg.syncflag;
&nbsp;&nbsp;}
}
static&nbsp;void&nbsp;mg_mgr_handle_ctl_sock(struct&nbsp;mg_mgr&nbsp;*mgr)&nbsp;{
	struct&nbsp;ctl_msg&nbsp;ctl_msg;
	size_t&nbsp;dummy;
	int&nbsp;ret&nbsp;=&nbsp;1;
	int&nbsp;len&nbsp;=&nbsp;(int)MG_RECV_FUNC(mgr-&gt;ctl[1],&nbsp;(char&nbsp;*)&amp;ctl_msg,&nbsp;sizeof(ctl_msg),&nbsp;0);

	//不是同步执行&nbsp;就立刻返回
	if&nbsp;(ctl_msg.syncflag&nbsp;==&nbsp;0)
		dummy&nbsp;=&nbsp;MG_SEND_FUNC(mgr-&gt;ctl[1],&nbsp;(char&nbsp;*)&amp;ret,&nbsp;sizeof(int),&nbsp;0);
	
	DBG((&quot;read&nbsp;%d&nbsp;from&nbsp;ctl&nbsp;socket&quot;,&nbsp;len));
	(void)dummy;&nbsp;/*&nbsp;https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509&nbsp;*/
	if&nbsp;(len&nbsp;&gt;=&nbsp;(int)&nbsp;sizeof(ctl_msg.callback)&nbsp;&amp;&amp;&nbsp;ctl_msg.callback&nbsp;!=&nbsp;NULL)&nbsp;{
		struct&nbsp;mg_connection&nbsp;*nc;
		if&nbsp;(ctl_msg.connid&nbsp;==&nbsp;0)
		{
			//先广播所有正常的连接
			for&nbsp;(nc&nbsp;=&nbsp;mg_next(mgr,&nbsp;NULL);&nbsp;nc&nbsp;!=&nbsp;NULL;&nbsp;nc&nbsp;=&nbsp;mg_next(mgr,&nbsp;nc))&nbsp;{
				ctl_msg.callback(nc,&nbsp;MG_EV_POLL,
					ctl_msg.data&nbsp;MG_UD_ARG(nc-&gt;user_data));
			}
			//最后发送一个nc为NULL的回调&nbsp;表示广播结束了&nbsp;用于清理内存等等
			ctl_msg.callback(NULL,&nbsp;MG_EV_POLL,
				ctl_msg.data&nbsp;MG_UD_ARG(nc-&gt;user_data));
		}
		else
		{
			for&nbsp;(nc&nbsp;=&nbsp;mg_next(mgr,&nbsp;NULL);&nbsp;nc&nbsp;!=&nbsp;NULL;&nbsp;nc&nbsp;=&nbsp;mg_next(mgr,&nbsp;nc))&nbsp;{
				if&nbsp;(nc-&gt;connid&nbsp;==&nbsp;ctl_msg.connid)
					break;
			}
			if&nbsp;(nc&nbsp;==&nbsp;NULL)
				ret&nbsp;=&nbsp;0;
			ctl_msg.callback(nc,&nbsp;MG_EV_POLL,
				ctl_msg.data&nbsp;MG_UD_ARG(nc-&gt;user_data));
		}
	}

	//同步执行的情况下&nbsp;执行完毕了&nbsp;返回ret
	if&nbsp;(ctl_msg.syncflag)
		dummy&nbsp;=&nbsp;MG_SEND_FUNC(mgr-&gt;ctl[1],&nbsp;(char&nbsp;*)&amp;ret,&nbsp;sizeof(int),&nbsp;0);
}</pre><p>增加connid参数，可以指定connid来触发回调，如果connid为0那就是广播到所有connection（给connection初始化connid的时候不能设置为0）。</p><p>增加异步、同步控制变量(int *ret)，传入NULL还为异步执行，如果设置了就意味着同步发送，发送完毕后才返回。同步方式可以节省内存，异步发送需要新开辟内存。</p><p>回调变动：</p><p>&nbsp;&nbsp;&nbsp;&nbsp;指定connid：如果connection还活着，那么正常回调（一次），如果connection关闭了没了，那么回调的mg_connection*是个NULL，用于清理缓冲区</p><p>&nbsp;&nbsp;&nbsp;&nbsp;未指定connid：广播到所有connection上，并且在所有connection回调完毕后，附加一次mg_connection*为NULL的广播，表示广播结束，用于清理缓冲区</p><p><br/></p><p>&nbsp;</p><p><br/></p><p>小魔改：</p><p>上述保存connection的connid用的是mg_connection的user_data变量，但是我们实现业务肯定也有一堆数据要存储，而且不同类型的请求例如http和ws要保存的数据类型也不一样，区分处理很麻烦，随手魔改了mg_connection结构体，直接给他加上了connid这个变量。开始做第一版的时候，用了mg_connection结构体里面的sock这个变量作为connid，也就是socket的句柄，不过后来想想大并发下socket关闭后再建立新连接可能句柄是会重复的，那就失去唯一的作用了，所以新增了connid变量。</p><p><br/></p><p><br/></p><p>好啦，完事！</p><p><br/></p><p>用压力测试跑了下几十个并发，对比发现低并发下跟其他库比如libevent差不多，满足要求。</p><p><br/></p><p><br/></p><p>=========================================================</p><p>2021.06.25更新</p><p>虽然上述的修改满足了要求，但是却修改了mongoose官方的代码，而且改动很细节，所以后来直接优化了一版，将上述的方法封装到了自己的C++代码中，mongoose直接引用官方6.18版本的代码即可，无需任何改动。</p><p>具体实现为自己封装了一个_broadcast方法，不魔改官方的mg_broadcast了。。。</p><pre class="brush:cpp;toolbar:false">void&nbsp;FHttpServer::_broadcast(uint32_t&nbsp;connID,&nbsp;void*&nbsp;data,&nbsp;BROADCAST_CALLBACK&nbsp;cb)
{
	MG_BROADCAST_MSG&nbsp;ctlmsg;
	ctlmsg.connid&nbsp;=&nbsp;connID;
	ctlmsg.callback&nbsp;=&nbsp;cb;
	ctlmsg.data&nbsp;=&nbsp;data;

	send(m_broadsender,&nbsp;(const&nbsp;char*)&amp;ctlmsg,&nbsp;sizeof(ctlmsg),&nbsp;0);
};

//内部使用方法是在线程里面发送数据的时候，通个回调函数的方式通过lambda封装丢到主线程里面去干活
	m_pServer-&gt;_broadcast(m_connID,&nbsp;pSendData,&nbsp;[](struct&nbsp;mg_connection*&nbsp;nc,&nbsp;void*&nbsp;data)&nbsp;{
		std::string*&nbsp;buf&nbsp;=&nbsp;(std::string*)data;
		if&nbsp;(nc)
			mg_send(nc,&nbsp;buf-&gt;c_str(),&nbsp;(int)buf-&gt;length());

		delete&nbsp;buf;
	});</pre><p>=========================================================</p><p>&nbsp;</p><p>=========================================================</p><p>2021.07.23更新</p><p style="white-space: normal;">有朋友指出官方的6.18版本无法编译通过，发现还真的是有个结构体声明有问题，当时开发我拿的是假的6.18么？</p><p style="white-space: normal;">根据官方最后的6.18版本，重新调整。。。</p><p style="white-space: normal;">增加静态文件服务器模式StartFileServer方法，该方法不能与StartServer同时使用，二选一。静态文件本地路径要求是&quot;/&quot;分割，不能用windows的&quot;\&quot;。</p><p style="white-space: normal;">例如StartFileServer(&quot;127.0.0.1:8686&quot;, &quot;D:/&quot;);</p><p>=========================================================</p><p><br/></p><p>完整代码(2021.07.23更新)：</p><p>需要依赖无锁队列库：https://github.com/cameron314/concurrentqueue</p><p>如果不开启ssl则无其他第三方依赖库。</p><p>C++11标准，编译器为vs2013。<br/></p><p><br/></p><p><br/></p><p>FHttpServer.h</p><pre class="brush:cpp;toolbar:false">#pragma&nbsp;once

#include&nbsp;&lt;string&gt;
#include&nbsp;&lt;chrono&gt;
#include&nbsp;&lt;vector&gt;
#include&nbsp;&lt;map&gt;
#include&nbsp;&lt;thread&gt;
#include&nbsp;&lt;mutex&gt;
#include&nbsp;&lt;functional&gt;
#include&nbsp;&quot;concurrentqueue/blockingconcurrentqueue.h&quot;&nbsp;//from&nbsp;https://github.com/cameron314/concurrentqueue


//如果需要ssl支持，开启下面的宏
//#define&nbsp;MG_ENABLE_SSL&nbsp;1

#include&nbsp;&quot;mongoose.h&quot;


typedef&nbsp;std::vector&nbsp;&lt;std::pair&lt;std::string,&nbsp;std::string&gt;&gt;&nbsp;&nbsp;StringPairArray;


//每个连接的userdata
typedef&nbsp;struct
{
	uint32_t&nbsp;connID;
	std::string&nbsp;uri;&nbsp;//ws依赖&nbsp;用于回调的map&nbsp;key
	std::string&nbsp;wsname;//ws依赖&nbsp;用于广播&nbsp;类似组名&nbsp;可重复
}MG_CONNECT_USERDATA;


//多线程任务队列
typedef&nbsp;struct
{
	union&nbsp;socket_address&nbsp;saddr;
	//uint32_t&nbsp;connFlags;
	uint32_t&nbsp;connID;
	int&nbsp;event_type;

	std::string&nbsp;body;&nbsp;//如果是websocket&nbsp;这里是data
	std::string&nbsp;method;
	std::string&nbsp;uri;
	std::string&nbsp;query;
	std::string&nbsp;proto;
	StringPairArray&nbsp;headers;
	int&nbsp;wsflags;&nbsp;&nbsp;//ws&nbsp;frame事件中的flags
}MG_EVENT_DATA,&nbsp;*&nbsp;MG_EVENT_DATA_PTR;


class&nbsp;FHttpServer;


//http请求信息
class&nbsp;FHttpRequest
{
	//不可复制
	FHttpRequest(const&nbsp;FHttpRequest&amp;)&nbsp;=&nbsp;delete;
	FHttpRequest&amp;&nbsp;operator=(const&nbsp;FHttpRequest&amp;)&nbsp;=&nbsp;delete;

	MG_EVENT_DATA_PTR&nbsp;m_pEventData;

public:
	FHttpRequest(MG_EVENT_DATA_PTR&nbsp;eventdata)&nbsp;:&nbsp;m_pEventData(eventdata)&nbsp;{};
	~FHttpRequest()&nbsp;{};

	uint32_t&nbsp;GetConnID();
	std::string&nbsp;GetURI();
	std::string&nbsp;GetMethod();
	std::string&nbsp;GetRemoteIP();
	//从url上面得到参数
	std::string&nbsp;GetQueryParam(const&nbsp;char*&nbsp;name);
	//从post的数据中得到参数
	std::string&nbsp;GetPostParam(const&nbsp;char*&nbsp;name);
	//读取header
	std::string&nbsp;GetHeader(const&nbsp;char*&nbsp;name);
	//读取所有header
	const&nbsp;StringPairArray&amp;&nbsp;GetHeaders();
	std::string&nbsp;GetCookies();
	size_t&nbsp;GetDataLen();
	std::string&nbsp;GetData();

private:
	std::string&nbsp;GetParam(mg_str*&nbsp;query_string,&nbsp;const&nbsp;char*&nbsp;name);
};

//http返回值操作
class&nbsp;FHttpResponse
{
	//不可复制
	FHttpResponse(const&nbsp;FHttpResponse&amp;)&nbsp;=&nbsp;delete;
	FHttpResponse&amp;&nbsp;operator=(const&nbsp;FHttpResponse&amp;)&nbsp;=&nbsp;delete;

	FHttpServer*&nbsp;m_pServer;
	uint32_t&nbsp;m_connID;
	int&nbsp;m_nStatusCode;
	std::string&nbsp;m_strStatus;
	StringPairArray*&nbsp;m_pDefaultHeaders;&nbsp;&nbsp;&nbsp;&nbsp;//默认的返回headers
	StringPairArray&nbsp;m_arrHeaders;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//如果修改了，那么使用自己的副本

public:
	FHttpResponse(uint32_t&nbsp;connID,&nbsp;FHttpServer*&nbsp;pServer,&nbsp;StringPairArray*&nbsp;defaultHeaders);
	~FHttpResponse();
	//是否已经发送过返回值了
	bool&nbsp;m_bsenddata;

	//设置http返回code和reason，要在Send之前调用
	void&nbsp;SetHttpStatusCode(int&nbsp;nStatusCode,&nbsp;const&nbsp;char*&nbsp;reason);
	//设置header&nbsp;唯一header名&nbsp;要在Send之前调用
	void&nbsp;SetHttpHeader(const&nbsp;std::string&amp;&nbsp;key,&nbsp;const&nbsp;std::string&amp;&nbsp;value);
	//追加header，类似多个SetCookie这种头部&nbsp;要在Send之前调用
	void&nbsp;AppendHttpHeader(const&nbsp;std::string&amp;&nbsp;key,&nbsp;const&nbsp;std::string&amp;&nbsp;value);

	//发送320转向
	void&nbsp;SetHttpRedirect(const&nbsp;std::string&amp;&nbsp;rurl);

	//发送数据&nbsp;直接发送
	void&nbsp;SendHttpContent(const&nbsp;std::string&amp;&nbsp;data,&nbsp;const&nbsp;char*&nbsp;type&nbsp;=&nbsp;NULL);

	//以chunk方式发送数据，调用SendChunkContent发送数据，不要直接用SendContent
	void&nbsp;SendHttpChunkBegin();
	//发送chunk数据，结束chunk发送一个空buf
	void&nbsp;SendHttpChunkContent(const&nbsp;std::string&amp;&nbsp;data);


private:
	void&nbsp;_SendData(const&nbsp;std::string*&nbsp;content);
	void&nbsp;_MakeResponseHeaders(std::string*&nbsp;data);
};

//websocket的消息信息
class&nbsp;FWSMessage
{
	MG_EVENT_DATA_PTR&nbsp;m_pEventData;

public:
	FWSMessage(MG_EVENT_DATA_PTR&nbsp;eventdata)&nbsp;:&nbsp;m_pEventData(eventdata)&nbsp;{};
	~FWSMessage()&nbsp;{};

	uint32_t&nbsp;GetConnID();
	std::string&nbsp;GetMsg();
	int&nbsp;GetFlag();
};

//websocket回话操作
class&nbsp;FWSSession
{
	FHttpServer*&nbsp;m_pServer;
	uint32_t&nbsp;m_connID;
public:
	FWSSession(uint32_t&nbsp;connID,&nbsp;FHttpServer*&nbsp;pServer)&nbsp;:
		m_connID(connID),
		m_pServer(pServer)&nbsp;{
	};
	~FWSSession()&nbsp;{};

	//设置该ws连接的name
	void&nbsp;SetName(const&nbsp;std::string&amp;&nbsp;name);

	//发送websocket数据
	void&nbsp;SendFrameMsg(const&nbsp;std::string&amp;&nbsp;data,&nbsp;int&nbsp;type&nbsp;=&nbsp;WEBSOCKET_OP_TEXT);

	//关闭连接
	void&nbsp;CloseConnect();
};

class&nbsp;FHttpServer
{
	//静态文件服务器
	struct&nbsp;mg_serve_http_opts&nbsp;m_mHttpServerOpts;
	//mg_serve_http_opts傻逼结构体用的是指针，还要保存下来字符串变量
	std::string&nbsp;m_strlocalpath;
	bool&nbsp;m_bisfileserver;

	struct&nbsp;mg_mgr&nbsp;m_mMgr;
	bool&nbsp;m_exitflag;

	//线程之间广播使用
	typedef&nbsp;void(*BROADCAST_CALLBACK)(struct&nbsp;mg_connection*&nbsp;nc,&nbsp;void*&nbsp;ev_data);
	typedef&nbsp;struct
	{
		BROADCAST_CALLBACK&nbsp;callback;
		uint32_t&nbsp;connid;&nbsp;&nbsp;//connID，可以遍历指定的connection
		void*&nbsp;data;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//传递的数据
	}MG_BROADCAST_MSG;
	sock_t&nbsp;m_broadsender;

	uint32_t&nbsp;m_nSeqConnid;

	//默认的返回headers
	StringPairArray&nbsp;m_defaultHeaders;

	//工作线程和队列
	moodycamel::BlockingConcurrentQueue&lt;MG_EVENT_DATA_PTR&gt;&nbsp;m_TaskQueue;
	std::vector&nbsp;&lt;std::thread&gt;&nbsp;m_threadPool;

	//http回调方法和映射表
	typedef&nbsp;std::function&lt;void(FHttpRequest&amp;,&nbsp;FHttpResponse&amp;)&gt;&nbsp;funcHttpCallback;
	std::map&nbsp;&lt;std::string,&nbsp;funcHttpCallback&gt;&nbsp;m_mapHttpRouter;
	//websocket回调方法和映射表
	typedef&nbsp;std::function&lt;void(FHttpRequest&amp;,&nbsp;FWSSession&amp;)&gt;&nbsp;funcWSOnReadyCallback;
	typedef&nbsp;std::function&lt;void(FWSMessage&amp;,&nbsp;FWSSession&amp;)&gt;&nbsp;funcWSOnMsgCallback;
	typedef&nbsp;std::function&lt;void(uint32_t)&gt;&nbsp;funcWSOnCloseCallback;
	struct&nbsp;stWebSocketCallback
	{
		funcWSOnReadyCallback&nbsp;onready;
		funcWSOnMsgCallback&nbsp;onmessage;
		funcWSOnCloseCallback&nbsp;onclose;
	};
	std::map&nbsp;&lt;std::string,&nbsp;stWebSocketCallback&gt;&nbsp;m_mapWSRouter;

public:
	FHttpServer();
	~FHttpServer();

	bool&nbsp;StartFileServer(const&nbsp;std::string&amp;&nbsp;addr,&nbsp;const&nbsp;std::string&amp;&nbsp;localpath);
	//启动服务&nbsp;addr可以是&quot;8888&quot;,&nbsp;&quot;:8888&quot;,&nbsp;&quot;127.0.0.1:8888&quot;;&nbsp;nThread为工作线程数
	bool&nbsp;StartServer(const&nbsp;std::string&amp;&nbsp;addr,&nbsp;int&nbsp;nThread);
	//停止服务
	void&nbsp;StopServer();
	//添加默认返回的http头，唯一值
	void&nbsp;AddDefaultHeader(const&nbsp;std::string&amp;&nbsp;key,&nbsp;const&nbsp;std::string&amp;&nbsp;value);
	//添加http回调接口
	void&nbsp;AddHttpHandler(const&nbsp;std::string&amp;&nbsp;uri,&nbsp;funcHttpCallback&nbsp;callback);
	//添加websocket回调接口
	void&nbsp;AddWSHandler(const&nbsp;std::string&amp;&nbsp;uri,
		funcWSOnReadyCallback&nbsp;onready,
		funcWSOnMsgCallback&nbsp;onmessage,
		funcWSOnCloseCallback&nbsp;onclose);

	//////////////////////////////////////////////////////////////////////////
	//websocket使用的接口
	//////////////////////////////////////////////////////////////////////////
	//发送数据到connID这个ws连接上
	void&nbsp;SendWSFrame(uint32_t&nbsp;connID,&nbsp;const&nbsp;std::string&amp;&nbsp;data,&nbsp;int&nbsp;type&nbsp;=&nbsp;WEBSOCKET_OP_TEXT);
	//关闭ws连接
	void&nbsp;CloseWSConn(uint32_t&nbsp;connID);
	//广播数据到name这个组
	void&nbsp;BroadcastWS(const&nbsp;std::string&amp;&nbsp;name,&nbsp;const&nbsp;std::string&amp;&nbsp;data,&nbsp;int&nbsp;type&nbsp;=&nbsp;WEBSOCKET_OP_TEXT);
	//////////////////////////////////////////////////////////////////////////


	//内部使用的接口，外部不要调用
	void&nbsp;_settaskquque(MG_EVENT_DATA_PTR&nbsp;eventdata);
	bool&nbsp;_ishttphandler(const&nbsp;std::string&amp;&nbsp;uri);
	bool&nbsp;_iswshandler(const&nbsp;std::string&amp;&nbsp;uri);
	bool&nbsp;_isstop();
	void&nbsp;_broadcast(uint32_t&nbsp;connID,&nbsp;void*&nbsp;data,&nbsp;BROADCAST_CALLBACK&nbsp;cb);

private:
	static&nbsp;void&nbsp;onEventCallback(mg_connection*&nbsp;nc,&nbsp;int&nbsp;event_type,&nbsp;void*&nbsp;event_data);
	void&nbsp;TaskQuqueRunner();
	void&nbsp;WorkEventHandler(MG_EVENT_DATA_PTR&nbsp;eventdata);
};</pre><p><br/></p><p>FHttpServer.cpp</p><pre class="brush:cpp;toolbar:false">#include&nbsp;&quot;stdafx.h&quot;
#include&nbsp;&quot;FHttpServer.h&quot;


#define&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;is_websocket(nc)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(nc-&gt;flags&nbsp;&amp;&nbsp;MG_F_IS_WEBSOCKET)
#define&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MG_REQUEST_MAXLENGTH&nbsp;&nbsp;&nbsp;&nbsp;10&nbsp;*&nbsp;1024&nbsp;*&nbsp;1024&nbsp;&nbsp;&nbsp;//10M

FHttpServer::FHttpServer()
{
	mg_mgr_init(&amp;m_mMgr,&nbsp;this);

	m_bisfileserver&nbsp;=&nbsp;false;
	m_nSeqConnid&nbsp;=&nbsp;1;
	m_exitflag&nbsp;=&nbsp;false;

	//默认返回的http头
	AddDefaultHeader(&quot;Content-Type&quot;,&nbsp;&quot;text/plain;&nbsp;charset=GBK&quot;);
	AddDefaultHeader(&quot;Connection&quot;,&nbsp;&quot;Keep-Alive&quot;);
}


FHttpServer::~FHttpServer()
{
	StopServer();
}

bool&nbsp;FHttpServer::StartFileServer(const&nbsp;std::string&amp;&nbsp;addr,&nbsp;const&nbsp;std::string&amp;&nbsp;localpath)
{
	m_strlocalpath&nbsp;=&nbsp;localpath;
	memset(&amp;m_mHttpServerOpts,&nbsp;0,&nbsp;sizeof(struct&nbsp;mg_serve_http_opts));
	m_mHttpServerOpts.document_root&nbsp;=&nbsp;m_strlocalpath.c_str();
	m_mHttpServerOpts.enable_directory_listing&nbsp;=&nbsp;&quot;yes&quot;;

	m_bisfileserver&nbsp;=&nbsp;true;
	return&nbsp;StartServer(addr,&nbsp;1);
}

bool&nbsp;FHttpServer::StartServer(const&nbsp;std::string&amp;&nbsp;addr,&nbsp;int&nbsp;nThread)
{
	//mg_connection&nbsp;*connection&nbsp;=&nbsp;mg_bind(&amp;m_mMgr,&nbsp;addr.c_str(),&nbsp;FHttpServer::OnEventHandler,&nbsp;this);
	mg_connection*&nbsp;connection&nbsp;=&nbsp;mg_bind(&amp;m_mMgr,&nbsp;addr.c_str(),&nbsp;onEventCallback);
	if&nbsp;(connection&nbsp;==&nbsp;NULL)
	{
		printf(&quot;listen&nbsp;server&nbsp;%s&nbsp;err.\n&quot;,&nbsp;addr.c_str());
		return&nbsp;false;
	}

	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mg_register_http_endpoint(connection,&nbsp;&quot;/upload&quot;,&nbsp;[](struct&nbsp;mg_connection&nbsp;*nc,&nbsp;int&nbsp;ev,&nbsp;void&nbsp;*p){
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;mg_http_multipart_part&nbsp;*mp&nbsp;=&nbsp;(struct&nbsp;mg_http_multipart_part&nbsp;*)&nbsp;p;
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;printf(&quot;endpoint&nbsp;eventtype:%d&nbsp;\n&quot;,&nbsp;ev,&nbsp;mp-&gt;status);
	//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;});

		//用于异步通知主线程干活&nbsp;不修改mongoose源码模拟和扩展mg_broadcast方法
	mg_connection*&nbsp;udprecv&nbsp;=&nbsp;mg_bind(&amp;m_mMgr,&nbsp;&quot;udp://127.0.0.1:0&quot;,&nbsp;[](mg_connection*&nbsp;nc,&nbsp;int&nbsp;event_type,&nbsp;void*&nbsp;event_data)
	{
		if&nbsp;(event_type&nbsp;==&nbsp;MG_EV_RECV)
		{
			struct&nbsp;mbuf*&nbsp;io&nbsp;=&nbsp;&amp;nc-&gt;recv_mbuf;
			if&nbsp;(io-&gt;len&nbsp;==&nbsp;sizeof(MG_BROADCAST_MSG))
			{
				MG_BROADCAST_MSG*&nbsp;pctlmsg&nbsp;=&nbsp;(MG_BROADCAST_MSG*)io-&gt;buf;
				if&nbsp;(pctlmsg-&gt;callback&nbsp;!=&nbsp;NULL)
				{

					struct&nbsp;mg_mgr*&nbsp;mgr&nbsp;=&nbsp;nc-&gt;mgr;
					struct&nbsp;mg_connection*&nbsp;nc;
					if&nbsp;(pctlmsg-&gt;connid&nbsp;==&nbsp;0)
					{
						//先广播所有正常的连接
						for&nbsp;(nc&nbsp;=&nbsp;mg_next(mgr,&nbsp;NULL);&nbsp;nc&nbsp;!=&nbsp;NULL;&nbsp;nc&nbsp;=&nbsp;mg_next(mgr,&nbsp;nc))
						{
							if&nbsp;(nc-&gt;user_data)
								pctlmsg-&gt;callback(nc,&nbsp;pctlmsg-&gt;data);
						}
						//最后发送一个nc为NULL的回调&nbsp;表示广播结束了&nbsp;用于清理内存等等
						pctlmsg-&gt;callback(NULL,&nbsp;pctlmsg-&gt;data);
					}
					else
					{
						for&nbsp;(nc&nbsp;=&nbsp;mg_next(mgr,&nbsp;NULL);&nbsp;nc&nbsp;!=&nbsp;NULL;&nbsp;nc&nbsp;=&nbsp;mg_next(mgr,&nbsp;nc))
						{
							if&nbsp;(nc-&gt;user_data&nbsp;&amp;&amp;&nbsp;((MG_CONNECT_USERDATA*)nc-&gt;user_data)-&gt;connID&nbsp;==&nbsp;pctlmsg-&gt;connid)
								break;
						}
						pctlmsg-&gt;callback(nc,&nbsp;pctlmsg-&gt;data);
					}
				}
			}

			//&nbsp;In&nbsp;case&nbsp;of&nbsp;UDP,&nbsp;Mongoose&nbsp;creates&nbsp;new&nbsp;virtual&nbsp;connection&nbsp;for
			//&nbsp;incoming&nbsp;messages
			//&nbsp;We&nbsp;can&nbsp;keep&nbsp;it&nbsp;(and&nbsp;it&nbsp;will&nbsp;be&nbsp;reused&nbsp;for&nbsp;another&nbsp;messages&nbsp;from
			//&nbsp;the&nbsp;same&nbsp;address)&nbsp;or&nbsp;we&nbsp;can&nbsp;close&nbsp;it&nbsp;(this&nbsp;saves&nbsp;some&nbsp;memory,&nbsp;but
			//&nbsp;decreases&nbsp;perfomance,&nbsp;because&nbsp;it&nbsp;forces&nbsp;creation&nbsp;of&nbsp;connection
			//&nbsp;for&nbsp;every&nbsp;incoming&nbsp;dgram)
			nc-&gt;flags&nbsp;|=&nbsp;MG_F_SEND_AND_CLOSE;
			//&nbsp;Discard&nbsp;message&nbsp;from&nbsp;recv&nbsp;buffer
			mbuf_remove(io,&nbsp;io-&gt;len);

		}//eventhandler
	});
	if&nbsp;(udprecv&nbsp;==&nbsp;NULL)
	{
		printf(&quot;listen&nbsp;broadcase&nbsp;channel&nbsp;err.\n&quot;);
		return&nbsp;false;
	}
	//建立发送者
	union&nbsp;socket_address&nbsp;broadrecvsa;
	socklen_t&nbsp;len&nbsp;=&nbsp;sizeof(broadrecvsa.sin);
	getsockname(udprecv-&gt;sock,&nbsp;&amp;broadrecvsa.sa,&nbsp;&amp;len);
	//建立发送者的udp&nbsp;socket
	union&nbsp;socket_address&nbsp;udpsasender;
	memset(&amp;udpsasender,&nbsp;0,&nbsp;sizeof(udpsasender));
	len&nbsp;=&nbsp;sizeof(udpsasender.sin);
	udpsasender.sin.sin_family&nbsp;=&nbsp;AF_INET;
	udpsasender.sin.sin_addr.s_addr&nbsp;=&nbsp;htonl(0x7f000001);&nbsp;/*&nbsp;127.0.0.1&nbsp;*/
	m_broadsender&nbsp;=&nbsp;socket(AF_INET,&nbsp;SOCK_DGRAM,&nbsp;0);
	if&nbsp;(m_broadsender&nbsp;==&nbsp;INVALID_SOCKET)
	{
		printf(&quot;make&nbsp;broadcase&nbsp;channel&nbsp;err.\n&quot;);
		return&nbsp;false;
	}
	if&nbsp;(bind(m_broadsender,&nbsp;&amp;udpsasender.sa,&nbsp;len)&nbsp;!=&nbsp;0)
	{
		printf(&quot;bind&nbsp;broadcase&nbsp;channel&nbsp;err.\n&quot;);
		return&nbsp;false;
	}
	if&nbsp;(connect(m_broadsender,&nbsp;&amp;broadrecvsa.sa,&nbsp;len)&nbsp;!=&nbsp;0)
	{
		printf(&quot;connect&nbsp;broadcase&nbsp;channel&nbsp;err.\n&quot;);
		return&nbsp;false;
	}

	//both&nbsp;http&nbsp;and&nbsp;websocket
	mg_set_protocol_http_websocket(connection);

	//初始化工作队列
	for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nThread;&nbsp;i++)
		m_threadPool.emplace_back(&amp;FHttpServer::TaskQuqueRunner,&nbsp;this);

	std::thread([this]&nbsp;{
		//&nbsp;loop
		while&nbsp;(!m_exitflag)
			mg_mgr_poll(&amp;m_mMgr,&nbsp;200);&nbsp;//&nbsp;ms
	}).detach();

	printf(&quot;started&nbsp;server&nbsp;on:&nbsp;%s(tid:%d)\n&quot;,&nbsp;addr.c_str(),&nbsp;GetCurrentThreadId());

	return&nbsp;true;
}

void&nbsp;FHttpServer::StopServer()
{
	if&nbsp;(m_exitflag)
		return;

	printf(&quot;set&nbsp;stop&nbsp;flag.\n&quot;);
	m_exitflag&nbsp;=&nbsp;true;

	printf(&quot;wait&nbsp;for&nbsp;work&nbsp;thread&nbsp;finish...\n&quot;);
	for&nbsp;(std::thread&amp;&nbsp;th&nbsp;:&nbsp;m_threadPool)
	{
		if&nbsp;(th.joinable())
			th.join();
	}

	printf(&quot;free&nbsp;mgr.\n&quot;);
	mg_mgr_free(&amp;m_mMgr);

	printf(&quot;server&nbsp;stopped.&quot;);
}

void&nbsp;FHttpServer::AddDefaultHeader(const&nbsp;std::string&amp;&nbsp;key,&nbsp;const&nbsp;std::string&amp;&nbsp;value)
{
	for&nbsp;(auto&amp;&nbsp;header&nbsp;:&nbsp;m_defaultHeaders)
	{
		if&nbsp;(mg_ncasecmp(key.c_str(),&nbsp;header.first.c_str(),&nbsp;key.length())&nbsp;==&nbsp;0)
		{
			header.second&nbsp;=&nbsp;value;
			return;
		}
	}

	m_defaultHeaders.emplace_back(key,&nbsp;value);
}

void&nbsp;FHttpServer::AddHttpHandler(const&nbsp;std::string&amp;&nbsp;uri,&nbsp;funcHttpCallback&nbsp;callback)
{
	m_mapHttpRouter.emplace(uri,&nbsp;callback);
}

void&nbsp;FHttpServer::AddWSHandler(const&nbsp;std::string&amp;&nbsp;uri,
	funcWSOnReadyCallback&nbsp;onready,
	funcWSOnMsgCallback&nbsp;onmessage,
	funcWSOnCloseCallback&nbsp;onclose)
{
	stWebSocketCallback&nbsp;cb&nbsp;=&nbsp;{&nbsp;onready,&nbsp;onmessage,&nbsp;onclose&nbsp;};
	m_mapWSRouter.emplace(uri,&nbsp;cb);
}

void&nbsp;FHttpServer::SendWSFrame(uint32_t&nbsp;connID,&nbsp;const&nbsp;std::string&amp;&nbsp;data,&nbsp;int&nbsp;type&nbsp;/*=&nbsp;WEBSOCKET_OP_TEXT*/)
{
	return&nbsp;FWSSession(connID,&nbsp;this).SendFrameMsg(data,&nbsp;type);
}

void&nbsp;FHttpServer::CloseWSConn(uint32_t&nbsp;connID)
{
	FWSSession(connID,&nbsp;this).CloseConnect();
}

void&nbsp;FHttpServer::BroadcastWS(const&nbsp;std::string&amp;&nbsp;name,&nbsp;const&nbsp;std::string&amp;&nbsp;data,&nbsp;int&nbsp;type&nbsp;/*=&nbsp;WEBSOCKET_OP_TEXT*/)
{
	struct&nbsp;wsData
	{
		int&nbsp;type;
		std::string&nbsp;name;
		std::string&nbsp;buf;
	};
	wsData*&nbsp;param&nbsp;=&nbsp;new&nbsp;wsData{&nbsp;type,&nbsp;name,&nbsp;data&nbsp;};

	//传入connid为0，广播到所有nc上
	_broadcast(0,&nbsp;param,&nbsp;[](struct&nbsp;mg_connection*&nbsp;nc,&nbsp;void*&nbsp;data)&nbsp;{
		if&nbsp;(nc)
		{
			if&nbsp;(is_websocket(nc))
			{
				wsData*&nbsp;p&nbsp;=&nbsp;(wsData*)data;
				MG_CONNECT_USERDATA*&nbsp;pUserdata&nbsp;=&nbsp;(MG_CONNECT_USERDATA*)nc-&gt;user_data;
				if&nbsp;(pUserdata-&gt;wsname&nbsp;==&nbsp;p-&gt;name)
					mg_send_websocket_frame(nc,&nbsp;p-&gt;type,&nbsp;p-&gt;buf.c_str(),&nbsp;p-&gt;buf.length());
			}
		}
		else
		{
			//nc为NULL就是广播完毕了&nbsp;这类清理内存
			delete&nbsp;((wsData*)data);
		}
	});
}

void&nbsp;FHttpServer::_settaskquque(MG_EVENT_DATA_PTR&nbsp;eventdata)
{
	m_TaskQueue.enqueue(eventdata);
}

bool&nbsp;FHttpServer::_ishttphandler(const&nbsp;std::string&amp;&nbsp;uri)
{
	if&nbsp;(m_mapHttpRouter.find(uri)&nbsp;==&nbsp;m_mapHttpRouter.end())
		return&nbsp;false;
	return&nbsp;true;
}

bool&nbsp;FHttpServer::_iswshandler(const&nbsp;std::string&amp;&nbsp;uri)
{
	if&nbsp;(m_mapWSRouter.find(uri)&nbsp;==&nbsp;m_mapWSRouter.end())
		return&nbsp;false;
	return&nbsp;true;
}

void&nbsp;FHttpServer::_broadcast(uint32_t&nbsp;connID,&nbsp;void*&nbsp;data,&nbsp;BROADCAST_CALLBACK&nbsp;cb)
{
	MG_BROADCAST_MSG&nbsp;ctlmsg;
	ctlmsg.connid&nbsp;=&nbsp;connID;
	ctlmsg.callback&nbsp;=&nbsp;cb;
	ctlmsg.data&nbsp;=&nbsp;data;

	send(m_broadsender,&nbsp;(const&nbsp;char*)&amp;ctlmsg,&nbsp;sizeof(ctlmsg),&nbsp;0);
};

bool&nbsp;FHttpServer::_isstop()
{
	return&nbsp;m_exitflag;
}

//单线程回调函数
void&nbsp;FHttpServer::onEventCallback(mg_connection*&nbsp;nc,&nbsp;int&nbsp;event_type,&nbsp;void*&nbsp;event_data)
{
	FHttpServer*&nbsp;pServer&nbsp;=&nbsp;(FHttpServer*)nc-&gt;mgr-&gt;user_data;
	MG_CONNECT_USERDATA*&nbsp;pUserData&nbsp;=&nbsp;(MG_CONNECT_USERDATA*)nc-&gt;user_data;

	//debug
&nbsp;&nbsp;&nbsp;//if&nbsp;(event_type&nbsp;!=&nbsp;MG_EV_POLL)
	//&nbsp;&nbsp;printf(&quot;EventCallback(%d):&nbsp;type:%d,&nbsp;sockid:%d\n&quot;,&nbsp;GetCurrentThreadId(),&nbsp;event_type,&nbsp;(pUserData&nbsp;?&nbsp;pUserData-&gt;connID&nbsp;:&nbsp;0));
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(event_type&nbsp;==&nbsp;MG_EV_RECV)
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;mbuf&nbsp;*io&nbsp;=&nbsp;&amp;nc-&gt;recv_mbuf;
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WriteBufferToFile(&quot;request.txt&quot;,&nbsp;io-&gt;buf,&nbsp;io-&gt;len,&nbsp;TRUE);
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}

	//连接上后&nbsp;创建出来connid等信息
	if&nbsp;(event_type&nbsp;==&nbsp;MG_EV_ACCEPT)
	{
		pUserData&nbsp;=&nbsp;new&nbsp;MG_CONNECT_USERDATA;
		pUserData-&gt;connID&nbsp;=&nbsp;pServer-&gt;m_nSeqConnid;

		nc-&gt;user_data&nbsp;=&nbsp;pUserData;
		//数据包最大限制
		//nc-&gt;recv_mbuf_limit&nbsp;=&nbsp;pServer-&gt;m_HttpMaxLength;

		pServer-&gt;m_nSeqConnid++;
		//connid使用0来标记&nbsp;所以要+1
		if&nbsp;(pServer-&gt;m_nSeqConnid&nbsp;==&nbsp;0)
			pServer-&gt;m_nSeqConnid++;

		return;
	}

	//关闭事件&nbsp;清理user_data
	if&nbsp;(event_type&nbsp;==&nbsp;MG_EV_CLOSE)
	{
		if&nbsp;(is_websocket(nc))
		{
			//websocket的话要回调
			MG_EVENT_DATA_PTR&nbsp;cbdata&nbsp;=&nbsp;new&nbsp;MG_EVENT_DATA;
			cbdata-&gt;event_type&nbsp;=&nbsp;event_type;
			cbdata-&gt;connID&nbsp;=&nbsp;pUserData-&gt;connID;
			cbdata-&gt;uri&nbsp;=&nbsp;pUserData-&gt;uri;

			pServer-&gt;_settaskquque(cbdata);
		}

		//删掉user_data
		delete&nbsp;pUserData;
		nc-&gt;user_data&nbsp;=&nbsp;NULL;

		return;
	}


	//http请求和websocket握手完毕&nbsp;要读取http&nbsp;request信息
	if&nbsp;(event_type&nbsp;==&nbsp;MG_EV_HTTP_REQUEST&nbsp;||
		event_type&nbsp;==&nbsp;MG_EV_WEBSOCKET_HANDSHAKE_REQUEST&nbsp;||
		event_type&nbsp;==&nbsp;MG_EV_WEBSOCKET_HANDSHAKE_DONE&nbsp;||
		event_type&nbsp;==&nbsp;MG_EV_WEBSOCKET_FRAME)
	{
		if&nbsp;(event_type&nbsp;==&nbsp;MG_EV_HTTP_REQUEST)
		{
			if&nbsp;(pServer-&gt;m_bisfileserver)
			{
				mg_serve_http(nc,&nbsp;(http_message*)event_data,&nbsp;pServer-&gt;m_mHttpServerOpts);
				return;
			}

			//处理映射表&nbsp;不在映射表的就不加入队列了
			std::string&nbsp;uri;
			uri.assign(((http_message*)event_data)-&gt;uri.p,&nbsp;((http_message*)event_data)-&gt;uri.len);
			auto&nbsp;iter&nbsp;=&nbsp;pServer-&gt;m_mapHttpRouter.find(uri);
			if&nbsp;(iter&nbsp;==&nbsp;pServer-&gt;m_mapHttpRouter.end())
			{
				//不支持的uri映射&nbsp;返回403吧
				mg_http_send_error(nc,&nbsp;403,&nbsp;&quot;Not&nbsp;Support.&quot;);
				return;
			}
		}
		else&nbsp;if&nbsp;(event_type&nbsp;==&nbsp;MG_EV_WEBSOCKET_HANDSHAKE_REQUEST)
		{
			//处理映射表&nbsp;不在映射表的就不加入队列了
			std::string&nbsp;uri;
			uri.assign(((http_message*)event_data)-&gt;uri.p,&nbsp;((http_message*)event_data)-&gt;uri.len);
			auto&nbsp;iter&nbsp;=&nbsp;pServer-&gt;m_mapWSRouter.find(uri);
			if&nbsp;(iter&nbsp;==&nbsp;pServer-&gt;m_mapWSRouter.end())
			{
				//不支持的uri映射&nbsp;返回403吧
				mg_http_send_error(nc,&nbsp;403,&nbsp;&quot;Not&nbsp;Support.&quot;);
			}

			return;
		}

		MG_EVENT_DATA_PTR&nbsp;cbdata&nbsp;=&nbsp;new&nbsp;MG_EVENT_DATA;
		cbdata-&gt;event_type&nbsp;=&nbsp;event_type;
		cbdata-&gt;connID&nbsp;=&nbsp;pUserData-&gt;connID;

		if&nbsp;(event_type&nbsp;==&nbsp;MG_EV_WEBSOCKET_FRAME)
		{
			websocket_message*&nbsp;wsmsg&nbsp;=&nbsp;(websocket_message*)event_data;
			cbdata-&gt;body.assign((char*)wsmsg-&gt;data,&nbsp;wsmsg-&gt;size);
			cbdata-&gt;wsflags&nbsp;=&nbsp;wsmsg-&gt;flags;
			//从user_data里面读取uri
			cbdata-&gt;uri&nbsp;=&nbsp;pUserData-&gt;uri;
		}
		else
		{
			http_message*&nbsp;httpmsg&nbsp;=&nbsp;(http_message*)event_data;

			//用于读取远程IP
			cbdata-&gt;saddr&nbsp;=&nbsp;nc-&gt;sa;

			//http&nbsp;request信息
			cbdata-&gt;body.assign(httpmsg-&gt;body.p,&nbsp;httpmsg-&gt;body.len);
			cbdata-&gt;method.assign(httpmsg-&gt;method.p,&nbsp;httpmsg-&gt;method.len);
			cbdata-&gt;uri.assign(httpmsg-&gt;uri.p,&nbsp;httpmsg-&gt;uri.len);
			cbdata-&gt;query.assign(httpmsg-&gt;query_string.p,&nbsp;httpmsg-&gt;query_string.len);
			cbdata-&gt;proto.assign(httpmsg-&gt;proto.p,&nbsp;httpmsg-&gt;proto.len);
			for&nbsp;(int&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;MG_MAX_HTTP_HEADERS;&nbsp;i++)
			{
				if&nbsp;(httpmsg-&gt;header_names[i].len&nbsp;==&nbsp;0)
					break;
				cbdata-&gt;headers.emplace_back(std::string(httpmsg-&gt;header_names[i].p,&nbsp;httpmsg-&gt;header_names[i].len),&nbsp;std::string(httpmsg-&gt;header_values[i].p,&nbsp;httpmsg-&gt;header_values[i].len));
			}

			//ws默认name就是uri&nbsp;可用于广播
			if&nbsp;(event_type&nbsp;==&nbsp;MG_EV_WEBSOCKET_HANDSHAKE_DONE)
				pUserData-&gt;wsname&nbsp;=&nbsp;pUserData-&gt;uri&nbsp;=&nbsp;cbdata-&gt;uri;
		}

		//printf(&quot;AddEvent&nbsp;Data(%d):&nbsp;%d\n&quot;,&nbsp;GetCurrentThreadId(),&nbsp;cbdata-&gt;event_type);
		pServer-&gt;_settaskquque(cbdata);
	}
}

void&nbsp;FHttpServer::TaskQuqueRunner()
{
	while&nbsp;(true)
	{
		//结束标记，退出线程
		if&nbsp;(m_exitflag)
			break;

		MG_EVENT_DATA_PTR&nbsp;eventdata;
		if&nbsp;(m_TaskQueue.wait_dequeue_timed(eventdata,&nbsp;std::chrono::seconds(1)))
		{
			//printf(&quot;TaskQuque&nbsp;Get&nbsp;Data(%d):&nbsp;%d\n&quot;,&nbsp;GetCurrentThreadId(),&nbsp;eventdata-&gt;event_type);
			WorkEventHandler(eventdata);

			//用完了要删掉
			delete&nbsp;eventdata;
		}
		else
		{
			//printf(&quot;Empty&nbsp;quque.\n&quot;);
		}
	}

	return;
}

void&nbsp;FHttpServer::WorkEventHandler(MG_EVENT_DATA_PTR&nbsp;eventdata)
{
	//printf(&quot;WorkHandler&nbsp;Data(%d):&nbsp;%d\n&quot;,&nbsp;GetCurrentThreadId(),&nbsp;eventdata-&gt;event_type);

	if&nbsp;(eventdata-&gt;event_type&nbsp;==&nbsp;MG_EV_HTTP_REQUEST)
	{
		//普通http请求
		&nbsp;//OnHttpHandler(eventdata);

		&nbsp;//准备变量
		FHttpRequest&nbsp;request(eventdata);
		FHttpResponse&nbsp;response(eventdata-&gt;connID,&nbsp;this,&nbsp;&amp;m_defaultHeaders);

		//回调
		m_mapHttpRouter[eventdata-&gt;uri](request,&nbsp;response);
		return;
	}

	//剩下的都是ws的事件了
	stWebSocketCallback&nbsp;wsCB&nbsp;=&nbsp;m_mapWSRouter[eventdata-&gt;uri];
	if&nbsp;(eventdata-&gt;event_type&nbsp;==&nbsp;MG_EV_WEBSOCKET_HANDSHAKE_DONE&nbsp;&amp;&amp;&nbsp;wsCB.onready)
	{
		FHttpRequest&nbsp;req(eventdata);
		FWSSession&nbsp;wssession(eventdata-&gt;connID,&nbsp;this);
		wsCB.onready(req,&nbsp;wssession);
	}
	else&nbsp;if&nbsp;(eventdata-&gt;event_type&nbsp;==&nbsp;MG_EV_WEBSOCKET_FRAME&nbsp;&amp;&amp;&nbsp;wsCB.onmessage)
	{
		FWSMessage&nbsp;wsmsg(eventdata);
		FWSSession&nbsp;wssession(eventdata-&gt;connID,&nbsp;this);
		wsCB.onmessage(wsmsg,&nbsp;wssession);
	}
	else&nbsp;if&nbsp;(eventdata-&gt;event_type&nbsp;==&nbsp;MG_EV_CLOSE&nbsp;&amp;&amp;&nbsp;wsCB.onclose)
	{
		wsCB.onclose(eventdata-&gt;connID);
	}
}

uint32_t&nbsp;FHttpRequest::GetConnID()
{
	return&nbsp;m_pEventData-&gt;connID;
}

std::string&nbsp;FHttpRequest::GetURI()
{
	return&nbsp;m_pEventData-&gt;uri;
}

std::string&nbsp;FHttpRequest::GetMethod()
{
	return&nbsp;m_pEventData-&gt;method;
}

std::string&nbsp;FHttpRequest::GetRemoteIP()
{
	char&nbsp;addr[32];
	mg_sock_addr_to_str(&amp;m_pEventData-&gt;saddr,&nbsp;addr,&nbsp;sizeof(addr),&nbsp;MG_SOCK_STRINGIFY_IP);
	return&nbsp;std::string(addr);
}

std::string&nbsp;FHttpRequest::GetParam(mg_str*&nbsp;query_string,&nbsp;const&nbsp;char*&nbsp;name)
{
	char&nbsp;value[128];
	int&nbsp;ret&nbsp;=&nbsp;mg_get_http_var(query_string,&nbsp;name,&nbsp;value,&nbsp;sizeof(value)&nbsp;-&nbsp;1);
	if&nbsp;(ret&nbsp;&lt;=&nbsp;0)
		return&nbsp;std::string();
	if&nbsp;(ret&nbsp;&lt;&nbsp;sizeof(value))
		return&nbsp;std::string(value,&nbsp;ret);

	//数据过长了
	std::unique_ptr&lt;char&gt;&nbsp;buf(new&nbsp;char[ret&nbsp;+&nbsp;1]);
	ret&nbsp;=&nbsp;mg_get_http_var(query_string,&nbsp;name,&nbsp;buf.get(),&nbsp;ret&nbsp;+&nbsp;1);
	return&nbsp;std::string(buf.get(),&nbsp;ret);
}

std::string&nbsp;FHttpRequest::GetQueryParam(const&nbsp;char*&nbsp;name)
{
	mg_str&nbsp;query_string;
	query_string.p&nbsp;=&nbsp;m_pEventData-&gt;query.c_str();
	query_string.len&nbsp;=&nbsp;m_pEventData-&gt;query.length();
	return&nbsp;GetParam(&amp;query_string,&nbsp;name);
}

std::string&nbsp;FHttpRequest::GetPostParam(const&nbsp;char*&nbsp;name)
{
	mg_str&nbsp;query_string;
	query_string.p&nbsp;=&nbsp;m_pEventData-&gt;body.c_str();
	query_string.len&nbsp;=&nbsp;m_pEventData-&gt;body.length();
	return&nbsp;GetParam(&amp;query_string,&nbsp;name);
}

std::string&nbsp;FHttpRequest::GetHeader(const&nbsp;char*&nbsp;name)
{
	for&nbsp;(auto&amp;&nbsp;header&nbsp;:&nbsp;m_pEventData-&gt;headers)
	{
		if&nbsp;(mg_ncasecmp(name,&nbsp;header.first.c_str(),&nbsp;header.first.length())&nbsp;==&nbsp;0)
			return&nbsp;header.second;
	}

	return&nbsp;std::string();
}

const&nbsp;StringPairArray&amp;&nbsp;FHttpRequest::GetHeaders()
{
	return&nbsp;m_pEventData-&gt;headers;
}

std::string&nbsp;FHttpRequest::GetCookies()
{
	return&nbsp;GetHeader(&quot;Cookie&quot;);
}

size_t&nbsp;FHttpRequest::GetDataLen()
{
	return&nbsp;m_pEventData-&gt;body.length();
}

std::string&nbsp;FHttpRequest::GetData()
{
	return&nbsp;m_pEventData-&gt;body;
}

FHttpResponse::FHttpResponse(uint32_t&nbsp;connID,&nbsp;FHttpServer*&nbsp;pServer,&nbsp;StringPairArray*&nbsp;defaultHeaders)&nbsp;:
	m_connID(connID),
	m_pServer(pServer),
	m_nStatusCode(200),
	m_strStatus(&quot;OK&quot;),
	m_pDefaultHeaders(defaultHeaders),
	m_bsenddata(false)
{

}

FHttpResponse::~FHttpResponse()
{
	if&nbsp;(!m_bsenddata)
		SendHttpContent(std::string());
}

void&nbsp;FHttpResponse::SetHttpStatusCode(int&nbsp;nStatusCode,&nbsp;const&nbsp;char*&nbsp;reason)
{
	m_nStatusCode&nbsp;=&nbsp;nStatusCode;
	m_strStatus&nbsp;=&nbsp;reason;
}

void&nbsp;FHttpResponse::SetHttpHeader(const&nbsp;std::string&amp;&nbsp;key,&nbsp;const&nbsp;std::string&amp;&nbsp;value)
{
	//修改了header&nbsp;就建立自己的副本
	if&nbsp;(m_pDefaultHeaders)
	{
		m_arrHeaders&nbsp;=&nbsp;*m_pDefaultHeaders;
		m_pDefaultHeaders&nbsp;=&nbsp;NULL;
	}

	for&nbsp;(auto&amp;&nbsp;header&nbsp;:&nbsp;m_arrHeaders)
	{
		if&nbsp;(mg_ncasecmp(key.c_str(),&nbsp;header.first.c_str(),&nbsp;key.length())&nbsp;==&nbsp;0)
		{
			header.second&nbsp;=&nbsp;value;
			return;
		}
	}

	m_arrHeaders.emplace_back(key,&nbsp;value);
}

void&nbsp;FHttpResponse::AppendHttpHeader(const&nbsp;std::string&amp;&nbsp;key,&nbsp;const&nbsp;std::string&amp;&nbsp;value)
{
	//修改了header&nbsp;就建立自己的副本
	if&nbsp;(m_pDefaultHeaders)
	{
		m_arrHeaders&nbsp;=&nbsp;*m_pDefaultHeaders;
		m_pDefaultHeaders&nbsp;=&nbsp;NULL;
	}

	m_arrHeaders.emplace_back(key,&nbsp;value);
}

void&nbsp;FHttpResponse::SetHttpRedirect(const&nbsp;std::string&amp;&nbsp;rurl)
{
	m_bsenddata&nbsp;=&nbsp;true;

	m_pServer-&gt;_broadcast(m_connID,&nbsp;new&nbsp;std::string(rurl),&nbsp;[](struct&nbsp;mg_connection*&nbsp;nc,&nbsp;void*&nbsp;data)&nbsp;{
		std::string*&nbsp;rurl&nbsp;=&nbsp;(std::string*)data;
		if&nbsp;(nc)
			mg_http_send_redirect(nc,&nbsp;302,&nbsp;mg_mk_str(rurl-&gt;c_str()),&nbsp;mg_mk_str(NULL));

		delete&nbsp;rurl;
	});
}

void&nbsp;FHttpResponse::SendHttpContent(const&nbsp;std::string&amp;&nbsp;data,&nbsp;const&nbsp;char*&nbsp;type&nbsp;/*=&nbsp;NULL*/)
{
	if&nbsp;(type)
		SetHttpHeader(&quot;Content-Type&quot;,&nbsp;type);

	_SendData(&amp;data);
}

void&nbsp;FHttpResponse::SendHttpChunkBegin()
{
	_SendData(NULL);
}

void&nbsp;FHttpResponse::SendHttpChunkContent(const&nbsp;std::string&amp;&nbsp;data)
{
	m_pServer-&gt;_broadcast(m_connID,&nbsp;new&nbsp;std::string(data),&nbsp;[](struct&nbsp;mg_connection*&nbsp;nc,&nbsp;void*&nbsp;data)&nbsp;{
		std::string*&nbsp;buf&nbsp;=&nbsp;(std::string*)data;
		if&nbsp;(nc)
			mg_send_http_chunk(nc,&nbsp;buf-&gt;c_str(),&nbsp;buf-&gt;length());

		delete&nbsp;buf;
	});
}

void&nbsp;FHttpResponse::_MakeResponseHeaders(std::string*&nbsp;data)
{
	//拼接header
	StringPairArray*&nbsp;headers&nbsp;=&nbsp;m_pDefaultHeaders;
	if&nbsp;(headers&nbsp;==&nbsp;NULL)
		headers&nbsp;=&nbsp;&amp;m_arrHeaders;
	size_t&nbsp;nHeanderSize&nbsp;=&nbsp;headers-&gt;size();
	for&nbsp;(size_t&nbsp;i&nbsp;=&nbsp;0;&nbsp;i&nbsp;&lt;&nbsp;nHeanderSize;&nbsp;i++)
	{
		std::string&nbsp;str&nbsp;=&nbsp;headers-&gt;at(i).first&nbsp;+&nbsp;&quot;:&nbsp;&quot;&nbsp;+&nbsp;headers-&gt;at(i).second&nbsp;+&nbsp;&quot;\r\n&quot;;
		data-&gt;append(str);
	}
}

void&nbsp;FHttpResponse::_SendData(const&nbsp;std::string*&nbsp;content)
{
	//不要重复发送
	if&nbsp;(m_bsenddata)
		return;

	std::string*&nbsp;pSendData&nbsp;=&nbsp;new&nbsp;std::string(&quot;HTTP/1.1&nbsp;&quot;&nbsp;+&nbsp;std::to_string(m_nStatusCode)&nbsp;+&nbsp;&quot;&nbsp;&quot;&nbsp;+&nbsp;m_strStatus&nbsp;+&nbsp;&quot;\r\n&quot;);
	_MakeResponseHeaders(pSendData);

	if&nbsp;(content&nbsp;==&nbsp;NULL)
	{
		//开启了chunk模式
		pSendData-&gt;append(&quot;Transfer-Encoding:&nbsp;chunked\r\n\r\n&quot;);
	}
	else
	{
		//有Content
		pSendData-&gt;append(&quot;Content-Length:&nbsp;&quot;&nbsp;+&nbsp;std::to_string(content-&gt;length())&nbsp;+&nbsp;&quot;\r\n\r\n&quot;);

		//拼接数据
		pSendData-&gt;append(*content);
	}

	//发送&nbsp;要用mg_broadcast来完成回调&nbsp;异步发送
	m_pServer-&gt;_broadcast(m_connID,&nbsp;pSendData,&nbsp;[](struct&nbsp;mg_connection*&nbsp;nc,&nbsp;void*&nbsp;data)&nbsp;{
		std::string*&nbsp;buf&nbsp;=&nbsp;(std::string*)data;
		if&nbsp;(nc)
			mg_send(nc,&nbsp;buf-&gt;c_str(),&nbsp;(int)buf-&gt;length());

		delete&nbsp;buf;
	});

	m_bsenddata&nbsp;=&nbsp;true;
}

uint32_t&nbsp;FWSMessage::GetConnID()
{
	return&nbsp;m_pEventData-&gt;connID;
}

std::string&nbsp;FWSMessage::GetMsg()
{
	return&nbsp;m_pEventData-&gt;body;
}

int&nbsp;FWSMessage::GetFlag()
{
	return&nbsp;m_pEventData-&gt;wsflags;
}

void&nbsp;FWSSession::SetName(const&nbsp;std::string&amp;&nbsp;name)
{
	m_pServer-&gt;_broadcast(m_connID,&nbsp;new&nbsp;std::string(name),&nbsp;[](struct&nbsp;mg_connection*&nbsp;nc,&nbsp;void*&nbsp;data)&nbsp;{
		std::string*&nbsp;name&nbsp;=&nbsp;(std::string*)data;
		if&nbsp;(nc)
			((MG_CONNECT_USERDATA*)nc-&gt;user_data)-&gt;wsname&nbsp;=&nbsp;*name;

		delete&nbsp;name;
	});
}

void&nbsp;FWSSession::SendFrameMsg(const&nbsp;std::string&amp;&nbsp;data,&nbsp;int&nbsp;type&nbsp;/*=&nbsp;WEBSOCKET_OP_TEXT*/)
{
	int&nbsp;ret&nbsp;=&nbsp;0;
	struct&nbsp;wsData
	{
		int&nbsp;type;
		std::string&nbsp;buf;
	};
	wsData*&nbsp;param&nbsp;=&nbsp;new&nbsp;wsData{&nbsp;type,&nbsp;data&nbsp;};

	//需要返回值&nbsp;所以使用同步的方式发送
	m_pServer-&gt;_broadcast(m_connID,&nbsp;param,&nbsp;[](struct&nbsp;mg_connection*&nbsp;nc,&nbsp;void*&nbsp;data)&nbsp;{
		wsData*&nbsp;p&nbsp;=&nbsp;(wsData*)data;
		if&nbsp;(nc)
			mg_send_websocket_frame(nc,&nbsp;p-&gt;type,&nbsp;p-&gt;buf.c_str(),&nbsp;p-&gt;buf.length());

		delete&nbsp;p;
	});
}

void&nbsp;FWSSession::CloseConnect()
{
	m_pServer-&gt;_broadcast(m_connID,&nbsp;NULL,&nbsp;[](struct&nbsp;mg_connection*&nbsp;nc,&nbsp;void*&nbsp;data)&nbsp;{
		if&nbsp;(nc)
			mg_send_websocket_frame(nc,&nbsp;WEBSOCKET_OP_CLOSE,&nbsp;&quot;bye&quot;,&nbsp;3);
	});
}</pre><p><br/></p><p>&nbsp;</p><p><br/></p><p>最后使用方法：</p><pre class="brush:cpp;toolbar:false">	FHttpServer&nbsp;server;
	server.AddHttpHandler(&quot;/hello&quot;,&nbsp;[](FHttpRequest&amp;&nbsp;req,&nbsp;FHttpResponse&amp;&nbsp;response)&nbsp;{
		printf(&quot;uri:%s\n&quot;,&nbsp;req.GetURI().c_str());
		printf(&quot;remote:%s\n&quot;,&nbsp;req.GetRemoteIP().c_str());
		printf(&quot;method:%s\n&quot;,&nbsp;req.GetMethod().c_str());
		printf(&quot;p:%s\n&quot;,&nbsp;req.GetQueryParam(&quot;p&quot;).c_str());
		printf(&quot;header&nbsp;host:%s\n&quot;,&nbsp;req.GetHeader(&quot;Host&quot;).c_str());
		//所有headers
		printf(&quot;headers:\n&quot;);
		for&nbsp;(const&nbsp;auto&amp;&nbsp;header&nbsp;:&nbsp;req.GetHeaders())
		{
			printf(&quot;\t%s:%s\n&quot;,&nbsp;header.first.c_str(),&nbsp;header.second.c_str());
		}
		printf(&quot;body(%d):%s\n&quot;,&nbsp;req.GetDataLen(),&nbsp;req.GetData().c_str());

		response.SendHttpContent(&quot;hello&nbsp;word!&quot;);
	});

	uint32_t&nbsp;connID&nbsp;=&nbsp;-1;
	server.AddWSHandler(&quot;/ws&quot;,&nbsp;[&amp;](FHttpRequest&amp;&nbsp;req,&nbsp;FWSSession&amp;&nbsp;session)
	{
		//websocket握手完毕连接建立
		printf(&quot;ws&nbsp;on&nbsp;ready&nbsp;%s.\n&quot;,&nbsp;req.GetQueryParam(&quot;id&quot;).c_str());
		session.SendFrameMsg(&quot;ready.&quot;);
		session.SetName(req.GetQueryParam(&quot;id&quot;));
		connID&nbsp;=&nbsp;req.GetConnID();
	},&nbsp;[](FWSMessage&amp;&nbsp;req,&nbsp;FWSSession&amp;&nbsp;session)
	{
		//接收到websocket消息
		printf(&quot;body:%s\n&quot;,&nbsp;req.GetMsg().c_str());
		session.SendFrameMsg(req.GetMsg());
	},[](uint32_t&nbsp;connID)
	{
		printf(&quot;ws&nbsp;on&nbsp;close.\n&quot;);
	});

	//不阻塞，返回bool
	server.StartServer(&quot;8881&quot;,&nbsp;32);

	while&nbsp;(true)
	{
		int&nbsp;x&nbsp;=&nbsp;getchar();
		if&nbsp;(x&nbsp;==&nbsp;&#39;q&#39;)
			break;

		//向已连接的websocket客户端发送数据，支持多线程调用发送数据
		server.SendWSFrame(connID,&nbsp;&quot;hello&quot;);

		//支持websocket组广播
		server.BroadcastWS(&quot;123&quot;,&nbsp;&quot;broadcase&nbsp;msg.&quot;);
	}

	server.StopServer();	</pre><p><br/></p><p><br/></p><p>访问：</p><p><a href="http://127.0.0.1:8881/hello?p=123" _src="http://127.0.0.1:8881/hello?p=123">http://127.0.0.1:8881/hello?p=123</a> </p><p><br/></p><p>输出：</p><p>&nbsp;uri:/hello</p><p>&nbsp;remote:127.0.0.1</p><p>&nbsp;method:GET</p><p>&nbsp;p:123</p><p>&nbsp;header host:127.0.0.1:8881</p><p>&nbsp;headers:</p><p>&nbsp; &nbsp; Host:127.0.0.1:8881</p><p>&nbsp; &nbsp; Connection:keep-alive</p><p>&nbsp; &nbsp; sec-ch-ua:&quot;Google Chrome&quot;;v=&quot;131&quot;, &quot;Chromium&quot;;v=&quot;131&quot;, &quot;Not_A Brand&quot;;v=&quot;24&quot;</p><p>&nbsp; &nbsp; sec-ch-ua-mobile:?0</p><p>&nbsp; &nbsp; sec-ch-ua-platform:&quot;Windows&quot;</p><p>&nbsp; &nbsp; DNT:1</p><p>&nbsp; &nbsp; Upgrade-Insecure-Requests:1</p><p>&nbsp; &nbsp; User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36</p><p>&nbsp; &nbsp; Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7</p><p>&nbsp; &nbsp; Sec-Fetch-Site:none</p><p>&nbsp; &nbsp; Sec-Fetch-Mode:navigate</p><p>&nbsp; &nbsp; Sec-Fetch-User:?1</p><p>&nbsp; &nbsp; Sec-Fetch-Dest:document</p><p>&nbsp; &nbsp; Accept-Encoding:gzip, deflate, br, zstd</p><p>&nbsp; &nbsp; Accept-Language:zh-CN,zh;q=0.9</p><p>&nbsp;body(0):</p><p><br/></p>]]></description><category>C/C++代码</category><comments>http://www.fenlog.com/post/126.html#comment</comments><wfw:commentRss>http://www.fenlog.com/feed.asp?cmt=126</wfw:commentRss></item></channel></rss>
