• 网狐定时器引擎分析


    先贴出来网狐源代码:

    #ifndef WH_THREAD_HEAD_FILE
    #define WH_THREAD_HEAD_FILE
    
    #pragma once
    
    #include "ServiceCoreHead.h"
    
    //////////////////////////////////////////////////////////////////////////////////
    
    //线程对象
    class SERVICE_CORE_CLASS CWHThread
    {
    	//状态变量
    private:
    	volatile bool					m_bRun;								//执行标志
    
    	//线程变量
    private:
    	UINT							m_uThreadID;						//线程标识
    	HANDLE							m_hThreadHandle;					//线程句柄
    
    	//函数定义
    protected:
    	//构造函数
    	CWHThread();
    	//析构函数
    	virtual ~CWHThread();
    
    	//接口函数
    public:
    	//获取状态
    	virtual bool IsRuning();
    	//启动线程
    	virtual bool StartThread();
    	//终止线程
    	virtual bool ConcludeThread(DWORD dwMillSeconds);
    
    	//功能函数
    public:
    	//线程标识
    	UINT GetThreadID() { return m_uThreadID; }
    	//线程句柄
    	HANDLE GetThreadHandle() { return m_hThreadHandle; }
    	//投递消息
    	LRESULT PostThreadMessage(UINT uMessage, WPARAM wParam, LPARAM lParam);
    
    	//事件函数
    protected:
    	//执行事件
    	virtual bool OnEventThreadRun() { return true; }
    	//開始事件
    	virtual bool OnEventThreadStrat() { return true; }
    	//终止事件
    	virtual bool OnEventThreadConclude() { return true; }
    
    	//内部函数
    private:
    	//线程函数
    	static unsigned __stdcall ThreadFunction(LPVOID pThreadData);
    };
    
    //////////////////////////////////////////////////////////////////////////////////
    
    #endif


    #include "StdAfx.h"
    #include "WHThread.h"
    
    //////////////////////////////////////////////////////////////////////////////////
    //结构定义
    
    //启动參数
    struct tagThreadParameter
    {
    	bool							bSuccess;							//是否错误
    	HANDLE							hEventFinish;						//事件句柄
    	CWHThread	*				pServiceThread;						//线程指针
    };
    
    //////////////////////////////////////////////////////////////////////////////////
    
    //构造函数
    CWHThread::CWHThread()
    {
    	//设置变量
    	m_bRun=false;
    	m_uThreadID=0;
    	m_hThreadHandle=NULL;
    
    	return;
    }
    
    //析构函数
    CWHThread::~CWHThread()
    {
    	//停止线程
    	ConcludeThread(INFINITE);
    
    	return;
    }
    
    //状态推断
    bool CWHThread::IsRuning()
    {
    	//执行检測
    	if (m_hThreadHandle==NULL) return false;
    	if (WaitForSingleObject(m_hThreadHandle,0)!=WAIT_TIMEOUT) return false;
    
    	return true;
    }
    
    //启动线程
    bool CWHThread::StartThread()
    {
    	//效验状态
    	ASSERT(IsRuning()==false);
    	if (IsRuning()==true) return false;
    
    	//清理变量
    	if (m_hThreadHandle!=NULL) 
    	{
    		//关闭句柄
    		CloseHandle(m_hThreadHandle);
    
    		//设置变量
    		m_uThreadID=0;
    		m_hThreadHandle=NULL;
    	}
     
    	//变量定义
    	tagThreadParameter ThreadParameter;
    	ZeroMemory(&ThreadParameter,sizeof(ThreadParameter));
    
    	//设置变量
    	ThreadParameter.bSuccess=false;
    	ThreadParameter.pServiceThread=this;
    	ThreadParameter.hEventFinish=CreateEvent(NULL,FALSE,FALSE,NULL);
    
    	//效验状态
    	ASSERT(ThreadParameter.hEventFinish!=NULL);
    	if (ThreadParameter.hEventFinish==NULL) return false;
    	
    	//启动线程
    	m_bRun=true;
    	m_hThreadHandle=(HANDLE)::_beginthreadex(NULL,0,ThreadFunction,&ThreadParameter,0,&m_uThreadID);
    
    	//错误推断
    	if (m_hThreadHandle==INVALID_HANDLE_VALUE)
    	{
    		CloseHandle(ThreadParameter.hEventFinish);
    		return false;
    	}
    
    	//等待事件
    	WaitForSingleObject(ThreadParameter.hEventFinish,INFINITE);
    	CloseHandle(ThreadParameter.hEventFinish);
    
    	//推断错误
    	if (ThreadParameter.bSuccess==false)
    	{
    		ConcludeThread(INFINITE);
    		return false;
    	}
    
    	return true;
    }
    
    //停止线程
    bool CWHThread::ConcludeThread(DWORD dwMillSeconds)
    {
    	//停止线程
    	if (IsRuning()==true)
    	{
    		//设置变量
    		m_bRun=false;
    
    		//停止等待
    		if (WaitForSingleObject(m_hThreadHandle,dwMillSeconds)==WAIT_TIMEOUT)
    		{
    			return false;
    		}
    	}
    
    	//设置变量
    	if (m_hThreadHandle!=NULL)
    	{
    		//关闭句柄
    		CloseHandle(m_hThreadHandle);
    
    		//设置变量
    		m_uThreadID=0;
    		m_hThreadHandle=NULL;
    	}
    
    	return true;
    }
    
    //投递消息
    LRESULT CWHThread::PostThreadMessage(UINT uMessage, WPARAM wParam, LPARAM lParam)
    {
    	//状态效验
    	ASSERT((m_uThreadID!=0)&&(m_hThreadHandle!=NULL));
    	if ((m_uThreadID==0)||(m_hThreadHandle==NULL)) return false;
    
    	//投递消息
    	if (::PostThreadMessage(m_uThreadID,uMessage,wParam,lParam)==FALSE)
    	{
    		DWORD dwLastError=GetLastError();
    		return dwLastError;
    	}
    
    	return 0L;
    }
    
    //线程函数
    unsigned __stdcall CWHThread::ThreadFunction(LPVOID pThreadData)
    {
    	//随机种子
    	srand((DWORD)time(NULL));
    
    	//变量定义
    	tagThreadParameter * pThreadParameter=(tagThreadParameter *)pThreadData;
    	CWHThread * pServiceThread=pThreadParameter->pServiceThread;
    
    	//启动通知
    	try
    	{
    		pThreadParameter->bSuccess=pServiceThread->OnEventThreadStrat(); 
    	} 
    	catch (...)
    	{
    		//设置变量
    		ASSERT(FALSE);
    		pThreadParameter->bSuccess=false;
    	}
    
    	//设置事件
    	bool bSuccess=pThreadParameter->bSuccess;
    	ASSERT(pThreadParameter->hEventFinish!=NULL);
    	if (pThreadParameter->hEventFinish!=NULL) SetEvent(pThreadParameter->hEventFinish);
    
    	//线程处理
    	if (bSuccess==true)
    	{
    		//线程执行
    		while (pServiceThread->m_bRun)
    		{
    #ifndef _DEBUG
    			//执行版本号
    			try
    			{
    				if (pServiceThread->OnEventThreadRun()==false)
    				{
    					break;
    				}
    			}
    			catch (...)	{ }
    #else
    			//调试版本号
    			if (pServiceThread->OnEventThreadRun()==false)
    			{
    				break;
    			}
    #endif
    		}
    
    		//停止通知
    		try
    		{ 
    			pServiceThread->OnEventThreadConclude();
    		} 
    		catch (...)	{ ASSERT(FALSE); }
    	}
    
    	//中止线程
    	_endthreadex(0L);
    
    	return 0L;
    }
    
    //////////////////////////////////////////////////////////////////////////////////
    

    #ifndef TIMERENGINE_HEAD_FILE
    #define TIMERENGINE_HEAD_FILE
    
    #pragma once
    
    //系统头文件
    
    #include "KernelEngineHead.h"
    #include "QueueServiceEvent.h"
    
    #define TIMER_SPACE								25				//时间间隔
    
    //类说明
    class CTimerEngine;
    
    //////////////////////////////////////////////////////////////////////////
    
    //定时器线程
    class CTimerThread : public CWHThread
    {
    	//变量定义
    protected:
    	CTimerEngine						* m_pTimerEngine;				//定时器引擎
    	//函数定义
    public:
    	//构造函数
    	CTimerThread(void);
    	//析构函数
    	virtual ~CTimerThread(void);
    
    	//功能函数
    public:
    	//配置函数
    	bool InitThread(CTimerEngine * pTimerEngine);
    
    	//重载函数
    private:
    	//执行函数
    	virtual bool OnEventThreadRun();
    };
    
    //////////////////////////////////////////////////////////////////////////
    
    //定时器子项
    struct tagTimerItem
    {
    	DWORD								wTimerID;						//定时器 ID
    	DWORD								dwElapse;						//定时时间
    	DWORD								dwTimeLeave;					//倒计时间
    	DWORD								dwRepeatTimes;					//反复次数
    	WPARAM								wBindParam;						//绑定參数
    };
    
    //定时器子项数组定义
    typedef CWHArray<tagTimerItem *> CTimerItemPtr;
    
    //////////////////////////////////////////////////////////////////////////
    
    //定时器引擎
    class CTimerEngine : public ITimerEngine
    {
    	friend class CTimerThread;
    
    	//状态变量
    protected:
    	bool								m_bService;						//执行标志
    	CTimerItemPtr						m_TimerItemFree;				//空暇数组
    	CTimerItemPtr						m_TimerItemActive;				//活动数组
    
    	//组件变量
    protected:
    	CCriticalSection					m_CriticalSection;				//线程锁
    	CTimerThread						m_TimerThread;					//定时器线程
    	IQueueServiceSink *					m_pIQueueServiceSink;			//通知回调
    
    	//函数定义
    public:
    	//构造函数
    	CTimerEngine(void);
    	//析构函数
    	virtual ~CTimerEngine(void);
    
    	//基础接口
    public:
    	//释放对象
    	virtual VOID Release() { if (IsValid()) delete this; }
    	//是否有效
    	virtual bool IsValid() { return AfxIsValidAddress(this, sizeof(CTimerEngine)) ? true : false; }
    	//接口查询
    	virtual void * QueryInterface(const IID & Guid, DWORD dwQueryVer);
    
    	//接口函数
    public:
    	//设置定时器
    	virtual bool SetTimer(DWORD dwTimerID, DWORD dwElapse, DWORD dwRepeat, WPARAM dwBindParameter);
    	//删除定时器
    	virtual bool KillTimer(DWORD dwTimerID);
    	//删除定时器
    	virtual bool KillAllTimer();
    	//设置接口
    	virtual bool SetTimerEngineEvent(IUnknownEx * pIUnknownEx);
    
    	//管理接口
    public:
    	//開始服务
    	virtual bool StartService();
    	//停止服务
    	virtual bool ConcludeService();
    
    	//内部函数
    private:
    	//定时器通知
    	void OnTimerThreadSink();
    };
    
    //////////////////////////////////////////////////////////////////////////
    
    #endif

    #include "StdAfx.h"
    #include "TimerEngine.h"
    #include "TraceService.h" //
    
    //宏定义
    #define NO_TIME_LEFT						DWORD(-1)				//不响应时间
    
    //////////////////////////////////////////////////////////////////////////
    
    //构造函数
    CTimerThread::CTimerThread(void)
    {
    	m_pTimerEngine = NULL;
    }
    
    //析构函数
    CTimerThread::~CTimerThread(void)
    {
    }
    
    //配置函数
    bool CTimerThread::InitThread(CTimerEngine * pTimerEngine)
    {
    	if (pTimerEngine == NULL) return false;
    
    	//设置变量
    	m_pTimerEngine = pTimerEngine;
    
    	return true;
    }
    
    //执行函数
    bool CTimerThread::OnEventThreadRun()
    {
    	ASSERT(m_pTimerEngine != NULL);
    	Sleep(TIMER_SPACE);
    	m_pTimerEngine->OnTimerThreadSink();
    	return true;
    }
    
    //////////////////////////////////////////////////////////////////////////
    
    //构造函数
    CTimerEngine::CTimerEngine(void)
    {
    	m_bService = false;
    	m_pIQueueServiceSink = NULL;
    }
    
    //析构函数
    CTimerEngine::~CTimerEngine(void)
    {
    	INT_PTR i = 0;
    	//停止服务
    	ConcludeService();
    
    	//清理内存
    	tagTimerItem * pTimerItem = NULL;
    	for (i = 0; i < m_TimerItemFree.GetCount(); i++)
    	{
    		pTimerItem = m_TimerItemFree[i];
    		ASSERT(pTimerItem != NULL);
    		SafeDelete(pTimerItem);
    	}
    	for (i = 0; i < m_TimerItemActive.GetCount(); i++)
    	{
    		pTimerItem = m_TimerItemActive[i];
    		ASSERT(pTimerItem != NULL);
    		SafeDelete(pTimerItem);
    	}
    	m_TimerItemFree.RemoveAll();
    	m_TimerItemActive.RemoveAll();
    
    	return;
    }
    
    //接口查询
    void * CTimerEngine::QueryInterface(const IID & Guid, DWORD dwQueryVer)
    {
    	QUERYINTERFACE(ITimerEngine, Guid, dwQueryVer);
    	QUERYINTERFACE_IUNKNOWNEX(ITimerEngine, Guid, dwQueryVer);
    	return NULL;
    }
    
    //设置定时器
    bool CTimerEngine::SetTimer(DWORD dwTimerID, DWORD dwElapse, DWORD dwRepeat, WPARAM dwBindParameter)
    {
    	DEBUG_GUARD;
    
    	//锁定资源
    	CWHDataLocker lock(m_CriticalSection);//
    
    	//效验參数
    	ASSERT(dwRepeat > 0L);
    	if (dwRepeat == 0) return false;
    
    	//查找定时器
    	bool bTimerExist = false;
    	tagTimerItem * pTimerItem = NULL;
    	for (INT_PTR i = 0; i < m_TimerItemActive.GetCount(); i++)
    	{
    		pTimerItem = m_TimerItemActive[i];
    		ASSERT(pTimerItem != NULL);
    		if (pTimerItem->wTimerID == dwTimerID)
    		{
    			bTimerExist = true;
    			break;
    		}
    	}
    
    	//创建定时器
    	if (bTimerExist == false)
    	{
    		INT_PTR nFreeCount = m_TimerItemFree.GetCount();
    		if (nFreeCount > 0)
    		{
    			pTimerItem = m_TimerItemFree[nFreeCount-1];
    			ASSERT(pTimerItem != NULL);
    			m_TimerItemFree.RemoveAt(nFreeCount - 1);
    		}
    		else
    		{
    			try
    			{
    				pTimerItem = new tagTimerItem;
    				ASSERT(pTimerItem != NULL);
    				if (pTimerItem == NULL) return false;
    			}
    			catch (...)
    			{
    				return false;
    			}
    		}
    	}
    
    	//设置參数
    	ASSERT(pTimerItem != NULL);
    	pTimerItem->wTimerID = dwTimerID;
    	pTimerItem->wBindParam = dwBindParameter;
    	pTimerItem->dwElapse = dwElapse;
    	pTimerItem->dwRepeatTimes = dwRepeat;
    
    	//提前20个粒度进行通知 - TIMER_SPACE * 20
    	if (pTimerItem->dwRepeatTimes == 1)
    		pTimerItem->dwTimeLeave = __max(TIMER_SPACE, pTimerItem->dwElapse - TIMER_SPACE * 20);
    	else
    		pTimerItem->dwTimeLeave = pTimerItem->dwElapse;
    
    	//激活定时器
    	if (bTimerExist == false)
    		m_TimerItemActive.Add(pTimerItem);
    
    	return true;
    }
    
    //删除定时器
    bool CTimerEngine::KillTimer(DWORD dwTimerID)
    {
    	DEBUG_GUARD;
    
    	//锁定资源
    	CWHDataLocker lock(m_CriticalSection);//
    
    	//查找定时器
    	tagTimerItem * pTimerItem = NULL;
    	for (INT_PTR i = 0; i < m_TimerItemActive.GetCount(); i++)
    	{
    		pTimerItem = m_TimerItemActive[i];
    		ASSERT(pTimerItem != NULL);
    		if (pTimerItem->wTimerID == dwTimerID)
    		{
    			m_TimerItemActive.RemoveAt(i);
    			m_TimerItemFree.Add(pTimerItem);
    			return true;;
    		}
    	}
    
    	return false;
    }
    
    //删除定时器
    bool CTimerEngine::KillAllTimer()
    {
    	//锁定资源
    	CWHDataLocker lock(m_CriticalSection);//
    
    	//删除定时器
    	m_TimerItemFree.Append(m_TimerItemActive);
    	m_TimerItemActive.RemoveAll();
    
    	return true;
    }
    
    //開始服务
    bool CTimerEngine::StartService()
    {
    	//效验状态
    	if (m_bService == true)
    	{
    		CTraceService::TraceString(TEXT("定时器引擎反复启动,启动操作忽略"), TraceLevel_Warning);
    		return true;
    	}
    
    	//设置变量
    	if (m_TimerThread.InitThread(this) == false)
    	{
    		CTraceService::TraceString(TEXT("定时器引擎线程服务初始化失败"), TraceLevel_Exception);
    		return false;
    	}
    
    	//启动服务
    	if (m_TimerThread.StartThread() == false)
    	{
    		CTraceService::TraceString(TEXT("定时器引擎线程服务启动失败"), TraceLevel_Exception);
    		return false;
    	}
    
    	SetThreadPriority(m_TimerThread.GetThreadHandle(), REALTIME_PRIORITY_CLASS);
    
    
    	//设置变量
    	m_bService = true;
    
    	return true;
    }
    
    //停止服务
    bool CTimerEngine::ConcludeService()
    {
    	//设置变量
    	m_bService = false;
    
    	//停止线程
    	m_TimerThread.ConcludeThread(INFINITE);
    
    	//设置变量
    	m_TimerItemFree.Append(m_TimerItemActive);
    	m_TimerItemActive.RemoveAll();
    
    	return true;
    }
    
    //设置接口
    bool CTimerEngine::SetTimerEngineEvent(IUnknownEx * pIUnknownEx)
    {
    	//效验參数
    	ASSERT(pIUnknownEx != NULL);
    	ASSERT(m_bService == false);
    	if (m_bService == true) return false;
    	if (pIUnknownEx == NULL) return false;
    
    	//设置接口
    	ASSERT(pIUnknownEx != NULL);
    	m_pIQueueServiceSink = QUERY_OBJECT_PTR_INTERFACE(pIUnknownEx, IQueueServiceSink);
    	ASSERT(m_pIQueueServiceSink != NULL);
    	return (m_pIQueueServiceSink != NULL);
    }
    
    //定时器通知
    void CTimerEngine::OnTimerThreadSink()
    {
    	DEBUG_GUARD;
    
    	//缓冲锁定
    	CWHDataLocker lock(m_CriticalSection);//
    
    	//查询定时器
    	tagTimerItem * pTimerItem = NULL;
    	for (INT_PTR i = m_TimerItemActive.GetCount() - 1; i >= 0; i--)
    	{
    		//效验參数
    		pTimerItem = m_TimerItemActive[i];
    		ASSERT(pTimerItem != NULL);
    		if (pTimerItem == NULL) return;
    		ASSERT(pTimerItem->dwTimeLeave > 0);
    		
    		//定时器处理
    		bool bKillTimer = false;
    		pTimerItem->dwTimeLeave -= TIMER_SPACE;
    		if (pTimerItem->dwTimeLeave <= 0L)
    		{
    			//设置次数
    			if (pTimerItem->dwRepeatTimes != TIMES_INFINITY)
    			{
    				ASSERT(pTimerItem->dwRepeatTimes > 0);
    				pTimerItem->dwRepeatTimes--;
    				if (pTimerItem->dwRepeatTimes == 0L)
    				{
    					bKillTimer = true;
    					m_TimerItemActive.RemoveAt(i);
    					m_TimerItemFree.Add(pTimerItem);
    				}
    			}
    
    			//设置时间
    			if (bKillTimer == false)//提前20个粒度进行通知 - TIMER_SPACE * 20
    			{
    				if (pTimerItem->dwRepeatTimes == 1)
    					pTimerItem->dwTimeLeave = __max(TIMER_SPACE, pTimerItem->dwElapse - TIMER_SPACE * 20);
    				else
    					pTimerItem->dwTimeLeave = pTimerItem->dwElapse;
    			}
    	
    			try
    			{
    				//BYTE cbBuffer[MAX_ASYNCHRONISM_DATA] = {0};		//接收缓冲//匿名
    				BYTE cbBuffer[sizeof(NTY_TimerEvent)] = {0};  //接收缓冲
    				//投递消息
    				NTY_TimerEvent * pTimerEvent = (NTY_TimerEvent *)cbBuffer;
    				pTimerEvent->dwTimerID = pTimerItem->wTimerID;
    				pTimerEvent->dwBindParameter = pTimerItem->wBindParam;
    				m_pIQueueServiceSink->OnQueueServiceSink(EVENT_TIMER, cbBuffer, sizeof(NTY_TimerEvent));
    			}
    			catch (...) { }
    		}
    	}
    }
    
    //////////////////////////////////////////////////////////////////////////
    
    //建立对象函数
    extern "C" __declspec(dllexport) void * CreateTimerEngine(const GUID & Guid, DWORD dwInterfaceVer)
    {
    	//建立对象
    	CTimerEngine * pTimerEngine = NULL;
    	try
    	{
    		pTimerEngine = new CTimerEngine();
    		if (pTimerEngine == NULL) throw TEXT("创建失败");
    		void * pObject = pTimerEngine->QueryInterface(Guid, dwInterfaceVer);
    		if (pObject == NULL) throw TEXT("接口查询失败");
    		return pObject;
    	}
    	catch (...) {}
    
    	//清理对象
    	SafeDelete(pTimerEngine);
    	return NULL;
    }
    
    //////////////////////////////////////////////////////////////////////////
    

    解读CWHThread类:
    说是CWHThread类感觉它更像一个接口,要想使用此类必须又一次写一个类继承它。重写OnEventThreadRun()函数,(OnEventThreadStrat和OnEventThreadConclude是可选重载)。不明确IsRuning()和StartThread()和ConcludeThread三个函数为什么设为虚函数,感觉这三个函数没有重写的必要。线程函数设为私有的。这是由于类对象不可能调用该函数,此函数仅仅共本类其它函数内部调用。由于线程函数要吗是全局函数,要吗是静态成员函数。本类就是一个线程,所以本类的线程函数是静态成员函数。

    这个静态成员函数有本类的StartThread成员函数调用。那么ThreadFunction去掉什么函数呢?让他掉用成员函数OnEventThreadRun。仅仅要我们子类又一次OnEventThreadRun函数就能够达到做不论什么业务的目的。那么问题就出来了,OnEventThreadRun函数是个静态成员函数。他怎样调用本类的非静态成员函数呢。本代码提供了一种非常好的机制。ThreadFunction函数加个參数,參数之中的一个是指向CWHThread实例的类指针,通过这个指针再去调用成员函数。长见识了。


    解读CWHThread类:


    类互相缠绕的问题:
    CWHThread的子类CTimerThread,专为CTimerEngine定制的子类,为什么这样说呢。由于类CTimerThread除了在CTimerEngine类里面有个实例外。此类不再会有其它实例。

    我们大眼一看,CTimerThread类里面包括了CTimerEngine类实例指针的属性,CTimerEngine里面包括了CTimerThread类的实例属性。这样的开发方式採用了impl技术。(类CTimerEngine为了訪问类CTimerThread的私有函数OnEventThreadRun把类CTimerThread加入为自己的友元类,再次不再赘述)。以下分析一下类互相缠绕的原因,类CTimerThread必须有个属性是CTimerEngine类的指针或者对象。由于仅仅有这样它才会知道线程要干什么活(干活方式是用自己的成员函数OnEventThreadRun调用类CTimerEngine的成员函数)。

    接着是类CTimerEngine为什么会包括类CTimerThread的实例,用通俗的话来说就是类CTimerEngine会告诉类CTimerThread给谁在干活,以及什么时候開始干,详细到代码就是CTimerEngine类成员函数StartService调用m_TimerThread.InitThread(this)告诉类CTimerThread在给谁干活,m_TimerThread.StartThread()告诉类CTimerThread什么时候開始干。







  • 相关阅读:
    UITextView 和 UITextField 的提示信息placeholder
    【转载】ios下的正则表达式,RegexKitLite
    Java集合(2)一 ArrayList 与 LinkList
    Java并发(2) 聊聊happensbefore
    Java并发(3) 聊聊Volatile
    Java并发(1) 聊聊Java内存模型
    Java集合(5)一 HashMap与HashSet
    Java集合(3)一 红黑树、TreeMap与TreeSet(上)
    Java集合(4)一 红黑树、TreeMap与TreeSet(下)
    Java集合(1)一 集合框架
  • 原文地址:https://www.cnblogs.com/cynchanpin/p/7201422.html
Copyright © 2020-2023  润新知