G
N
I
D
A
O
L

端口池(C++)

端口池

对齐有点乱,可以复制到自己的IDE自动对齐

PortPool.h

/**************************************************************************
 *
 *      Copyright (C) 2022
 *      All rights reserved
 *
 *      Filename    :	WLPortPool.h
 *      Description : 动态端口已占用完毕的情况下,获取动态端口的函数接口。
 *	Environment : 由于动态端口资源耗尽,导致主机卫士的curl无法通信,WLPortPool
 *		      可以在该情况下启动端口扫描的线程,在动态端口不满足()的情况下,创建端口池
 *		     (STU_PORT_POOL)。curl可以在此环境下调用此函数提供的接口获取系统当前可用端口。
 *
 *	Detail      : 接口命名空间(WNTPORT)。
 *		1. 使用时需先开启端口池扫描线程 createScanPortsThread,该线程需调用 killScanPortsThread 关闭
 *		2. 启动线程后,通过全局变量 WNTPORT::g_bFlagGetStaticPort 或者 getPortPoolStatus() 查看当前端口池状态
 *		3. 根据第2步的状态选择是否需要获取端口池,如需,调用 getPort()
 *      Created by  mingming.shi on Jan. 24th, 2022
 *
 *************************************************************************/
#pragma once

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <vector>
#include <sstream>
#include <windows.h>
#include <WinSock.h>
#include <tcpmib.h>
#include <IPHlpApi.h>
#include <set>
#include "../include/curl/include/curl.h"
#include "..\common\WLNetCommApi.h"

#pragma comment(lib, "WS2_32.lib")
#pragma comment(lib, "IPHlpApi.lib")

#define MAX_PORT_NUM		10		// 默认端口池的数量
#define DEFAULT_START_PORT	1025	// 默认启动端口
#define DEFAULT_STOP_PORT	65535	// 默认终止端口
#define DEFAULT_PORT_RANGE	100		// 默认端口范围
#define DEFAULT_MIN_PORTRANGE	50		// 默认最小端口范围
#define START_STATIC_PORT_FLAG	100		// FLAG:当系统可用动态端口个数小于该值时,启用端口池

/* 端口池结构体 */
typedef struct _PORT_POOL {
	UINT nStartPort;	// 启动端口
	UINT nRange;		// 端口范围
	bool bEnable;		// 保留:当前端口是否可用,用于回调接口时用户自行设置。false表示不可用

	_PORT_POOL(UINT nSta = 0, UINT nRan = 0, bool bEn = false):nStartPort(nSta),nRange(nRan),bEnable(bEn){};

	bool isContain(UINT uPORT)
	{
		if ( uPORT >= nStartPort && uPORT < (nStartPort + nRange) )
		{
			return true;
		}
		return false;
	}
	
	bool isLegal()
	{
		if( (this->nStartPort + this->nRange) <= DEFAULT_STOP_PORT &&
			(this->nStartPort) >= DEFAULT_START_PORT
			)
		{
			return true;
		}
		return false;
	}

	bool operator<(const _PORT_POOL& b) const
	{
		return this->nStartPort == b.nStartPort ? this->nRange <= b.nRange : this->nStartPort < b.nRange;
	}
}STU_PORT_POOL, *PSTU_PORT_POOL;

/* (端口池) */
extern std::vector<STU_PORT_POOL> g_stuPortPool;

#ifdef __cplusplus
extern "C" {
#endif
	namespace WNTPORT {
		extern bool g_bFlagGetStaticPort;	// 端口池是否启用。		[true:表示已经启用端口池]
		extern bool g_bStartThread;			// 端口检测线程是否开启。	[true:表示已经启动端口检测线程]
		extern int g_nCurPoint;				// 端口池轮询
		extern HANDLE g_hEvent;				// 端口池检测线程的控制句柄
		/* 端口池线程 */
		void createScanPortsThread();

		/* 杀死线程 */
		void killScanPortsThread();

#if 0 /* WLNetCommApi */
#endif
		/* 设置当前端口状态 */
		void WINAPI setPortIsAvailable(__in STU_PORT_POOL stuPort);

		/* 返回一个可用端口和其范围 */
		void WINAPI getPort(__out STU_PORT_POOL& stuPort);

		/* 返回端口池状态 */
		bool WINAPI getPortPoolStatus();

		/* 设置 curl 的启动端口和范围 */
		int WINAPI setCurlPort(CURL *curl);

		/* 设置 socket 的本地端口 */
		int WINAPI setSocketPort(SOCKADDR_IN *socket);

	}
#ifdef __cplusplus
}
#endif

PortPool.cpp

/**************************************************************************
 *
 *        Copyright (C) 2022  Beijing
 *        All rights reserved
 *
 *        Filename    :	WLPortPool.cpp
 *        Description : 动态端口已占用完毕的情况下,获取动态端口的函数接口实现文件,详看头文件声明
 *
 *        Created by  mingming.shi on Jan. 24th, 2022
 *
 *************************************************************************/
#include "StdAfx.h"
#include "WLPortPool.h"
#include <process.h>


#define STU_MEMBER_NUM			2
#define CMD_RESULT_BUF_SIZE		1024
#define TIME_SCAN_FREQUENCY		1000 * 30
#define CMD_QUERY_DYNAMIC_TCPPORT	"netsh int ipv4 show dynamicport tcp"

std::vector<STU_PORT_POOL> g_stuPortPool;

bool WNTPORT::g_bFlagGetStaticPort = false;
bool WNTPORT::g_bStartThread = false;
int WNTPORT::g_nCurPoint = 0;
HANDLE WNTPORT::g_hEvent = NULL;


#if 0 /* 内部函数声明 */
#endif
/************************************************************************/
/* 其他函数声明 2022-1-24 14:33:28                                    */
/************************************************************************/
// 获取动态端口
int executeCMD(__out char *pChRet);
int getDynamicPortFromstr(__out STU_PORT_POOL& stuDynamicPort);

// 获取当前已占用端口
int getAllTcpConnectionsPort(__out std::set<UINT>& setRet);

// 获取剩余端口
int getFreeDynamicPort(__in STU_PORT_POOL stuDynamicPort, __in std::set<UINT> CurSysUsedPort, __out std::vector<STU_PORT_POOL>& stuFreeDynamicPort);
int getFreeStaticPort(__in STU_PORT_POOL stuDynamicPort, __in std::set<UINT> CurSysUsedPort, __out std::vector<STU_PORT_POOL>& stuFreeStaticPort);

// 更新端口
void updatePort(__in __out std::vector<STU_PORT_POOL>::iterator it);

// 创建端口池
int createPortPool();

// 获取剩余动态端口数量 
int getFreeDynamicPortsNum(__out int& nNum);

// 线程:获取系统当前剩余动态端口数量
unsigned int WINAPI scanSysDynamicPortsNum(void* lpParam);

#if 0 /* 对外接口定义 */
#endif

/*
* @fn			createScanPortsThread
* @brief		启动端口检测线程,设置 g_bStartThread 句柄控制端口池线程的状态
* @param[in]    
* @param[out]	
* @return		
*               
* @detail      	调用 killScanPortsThread 关闭端口扫描线程
* @author		mingming.shi
* @date			2022-1-27
*/
void WNTPORT::createScanPortsThread()
{
	if( false == g_bStartThread )
	{
		HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, scanSysDynamicPortsNum, 0, 0, NULL);
		if (NULL == hThread)
		{
			WriteError(_T("Create scanSysDynamicPortsNum thread error. [%d]"), GetLastError());
			g_bStartThread = false;
			return;
		}
		WriteInfo(_T("Create scanSysDynamicPortsNum thread SUCCESS. "));

		g_hEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("PortPool"));
		CloseHandle(hThread);
		
		g_bStartThread = true;
	}

}

/*
* @fn			killScanPortsThread
* @brief		杀死扫描动态端口的线程
* @param[in]    
* @param[out]	
* @return		
*               
* @detail      	
* @author		mingming.shi
* @date			2022-1-28
*/
void WNTPORT::killScanPortsThread()
{
	SetEvent(g_hEvent);
	WriteInfo(_T("LINE [%d] killScanPortsThread"), __LINE__);
	return;
}

/*
* @fn			getPort
* @brief		返回一个可用端口和其范围(同时供 WLNetCommApi 使用)
* @param[in]	
* @param[out]   STU_PORT_POOL: 返回一个可用端口,如果调用失败,则修改stuPort中的BEnable为false
* @return		0表示成功;其他表示失败
*               
* @detail      	依次返回端口池中的端口和范围
* @author		mingming.shi
* @date			2022-1-24
*/
void WINAPI WNTPORT::getPort(__out STU_PORT_POOL& stuPort)
{
	int iRet = -1;
	std::vector<STU_PORT_POOL>::iterator it = g_stuPortPool.begin();

	if ( 0 == g_stuPortPool.size() ) // 当前端口池为空
	{
		iRet = createPortPool();
		if (NO_ERROR != iRet)
		{
			WriteError(_T("LINE [%d] Create Port Pool FAILED!"), __LINE__);
			return;
		}
	}

	it = g_stuPortPool.begin();
	for (int i = 0; it != g_stuPortPool.end(); it++)
	{
		if (false == it->isLegal())
		{
			updatePort(it);
		}

		if ( true == it->bEnable &&		// 当前端口可用
			true == it->isLegal() &&	// 当前端口合法
			i == g_nCurPoint % 10)			// 轮询
		{
			STU_PORT_POOL stuTemp;
			stuTemp = *it;

			stuPort.nStartPort = stuTemp.nStartPort;
			stuPort.nRange = stuTemp.nRange;
			stuPort.bEnable = stuTemp.bEnable;

			g_nCurPoint++;
			if (g_nCurPoint >= INT_MAX)
			{
				g_nCurPoint = 0;
			}
			break;
		}
		i++;
	}

}

/*
* @fn			setPortIsAvailable
* @brief		设置当前端口的可用状态(同时供 WLNetCommApi 使用)
* @param[in]	STU_PORT_POOL: 需要删除的端口
* @param[out]   
* @return		
*               
* @detail      	会根据当前的启动端口和范围在端口池中查找一致的并删除,然后立即更新端口池
*				【改接口保留,但目前未使用】
* @author		mingming.shi
* @date			2022-1-24
*/
void WINAPI WNTPORT::setPortIsAvailable(__in STU_PORT_POOL stuPort)
{
	std::vector<STU_PORT_POOL>::iterator it = g_stuPortPool.begin();
	for (; it != g_stuPortPool.end(); it++)
	{
		if (it->nStartPort == stuPort.nStartPort &&
			it->nRange == stuPort.nRange && 
			stuPort.bEnable == false) // 当前端口不可用
		{
			// g_stuPortPool.erase(it); // 目前不移除
			break;
		}
	}

	updatePort(it);
}

/*
* @fn			getPortPoolStatus
* @brief		获取端口池启用状态(同时供 WLNetCommApi 使用)
* @param[in]    
* @param[out]	
* @return		true表示已经启用端口池
*               
* @detail      	
* @author		mingming.shi
* @date			2022-1-28
*/
bool WINAPI WNTPORT::getPortPoolStatus()
{
	return g_bFlagGetStaticPort;
}

/*
* @fn			setCurlPort
* @brief		设置Curl的启动端口和范围
* @param[in]    curl:需要设置端口的CURL对象
* @param[out]	curl:设置了启动端口和范围的CURL对象
* @return		0表示成功;其他表示失败
*               
* @detail      	
* @author		mingming.shi
* @date			2022-2-10
*/
int WINAPI WNTPORT::setCurlPort(__in __out CURL *curl)
{
	int iRet = 0;
	std::set<UINT>	CurSysUsedPort;
	STU_PORT_POOL stuAllPortRange;
	STU_PORT_POOL stuSet2CurlPort;

	// 1 获取端口占用情况
	iRet = getAllTcpConnectionsPort(CurSysUsedPort);
	if(NO_ERROR != iRet)
	{
		iRet = -1;
		goto _END_;
	}

	// 2 从未占用的端口中获取一个启动地址和范围
	stuAllPortRange.nStartPort = DEFAULT_START_PORT;
	stuAllPortRange.nRange = DEFAULT_STOP_PORT;

	for (UINT nPort = DEFAULT_START_PORT; nPort != DEFAULT_STOP_PORT; ++nPort)
	{
		if ( CurSysUsedPort.end() == CurSysUsedPort.find(nPort) && true == stuAllPortRange.isContain(nPort) ) // 找到了未使用端口,并且不在动态端口范围内
		{
			int nRange = 0;

			// 遍历当前启动端口的端口范围
			do
			{
				nRange++;
			}while ( 
				CurSysUsedPort.end() ==  CurSysUsedPort.find(nPort + nRange) // 系统未使用
				&& nRange < DEFAULT_PORT_RANGE); // 范围小于默认端口范围

			stuSet2CurlPort.nStartPort	= nPort;
			stuSet2CurlPort.nRange		= nRange >= DEFAULT_MIN_PORTRANGE ? nRange : DEFAULT_MIN_PORTRANGE;
		}
	}

	// 3 设置curl的启动端口和范围
	curl_easy_setopt(curl, CURLOPT_LOCALPORT, stuPort.nStartPort);
	curl_easy_setopt(curl, CURLOPT_LOCALPORTRANGE, stuPort.nRange);
	iRet = 0;

_END_:
	return iRet;
}

/*
* @fn			setSocketPort
* @brief		绑定一个本地可用端口到socket
* @param[in]    socket:需要绑定本地端口的socket
* @param[out]	socket:绑定了本地端口的socket
* @return		0表示成功;其他表示失败
*               
* @detail      	
* @author		mingming.shi
* @date			2022-2-10
*/
int WINAPI WNTPORT::setSocketPort(__in __out SOCKADDR_IN *socket)
{
	int iRet;
	int iBindRet;
	SOCKADDR_IN myaddr;
	std::set<UINT>	CurSysUsedPort;
	UINT nPort = DEFAULT_START_PORT;

	// 1 获取端口占用情况
	iRet = getAllTcpConnectionsPort(CurSysUsedPort);
	if(NO_ERROR != iRet)
	{
		iRet = -1;
		goto _END_;
	}

	// 2 从未占用的端口中获取一个可用端口并设置

	for ( nPort != DEFAULT_STOP_PORT; ++nPort)
	{
		if ( CurSysUsedPort.end() == CurSysUsedPort.find(nPort) && true == stuAllPortRange.isContain(nPort) ) // 找到了未使用端口,并且不在动态端口范围内
		{
			break;
		}
	}
	
	// 绑定 nPort 到 sokcet
	myaddr.sin_port = htons(nPort);
	myaddr.sin_addr.s_addr = INADDR_ANY;
	myaddr.sin_family = AF_INET;
	iBindRet = bind(m_Socket, (SOCKADDR*)&myaddr, sizeof( struct sockaddr));
	if (iBindRet < 0)
	{
		iRet = -2;
		goto _END_;
	}	
	
	
_END_:
	return iRet;
}

#if 0 /* 内部函数定义 */
#endif

/*
* @fn			CreatPortPool
* @brief		创建端口资源池
* @param[in]    
* @param[out]	
* @return		0:成功;其他:表示失败
*               
* @detail      	当系统动态端口使用情况不满足IEG时调用此接口获取静态端口资源池,保存到 g_stuPortPool 变量中
* @author		mingming.shi
* @date			2022-1-24
*/
int createPortPool()
{
	int iRet = 0;
	std::vector<STU_PORT_POOL> stuPortPool;			// 保存接口返回的端口范围
	std::vector<STU_PORT_POOL> stuFreeDynamicPort;	// 保存未被使用端口范围
	STU_PORT_POOL	stuDynamicPort;
	std::set<UINT>	CurSysUsedPort;

	// 1 获取动态端口状态(起始地址和范围)
	iRet = getDynamicPortFromstr(stuDynamicPort);
	if(NO_ERROR != iRet)
	{
		iRet = -1;
		goto _END_;
	}

	// 2 获取端口占用情况
	iRet = getAllTcpConnectionsPort(CurSysUsedPort);
	if(NO_ERROR != iRet)
	{
		iRet = -2;
		goto _END_;
	}

	// 3 从默认地址开始查找可用端口范围
	iRet = getFreeStaticPort(stuDynamicPort, CurSysUsedPort, stuFreeDynamicPort);
	if(NO_ERROR == iRet)
	{
		g_stuPortPool = stuFreeDynamicPort;
	}

	iRet = 0;
_END_:
	return iRet;
}

/*
* @fn			ExecuteCMD
* @brief		执行查询TCP动态端口命令,获取系统动态端口范围
* @param[in]    
* @param[out]	pChRet: 命令行执行后的输出字符串
* @return		0 表示成功;其他 失败
*               
* @detail      	
* @author		mingming.shi
* @date			2022-1-24
*/
static int executeCMD(__out char *pChRet)
{
	int iRet = -1;
	char buf_ps[CMD_RESULT_BUF_SIZE];
	char pChBuf[CMD_RESULT_BUF_SIZE] = { 0 };
	FILE *ptr;

	strcpy_s(pChBuf, _countof(CMD_QUERY_DYNAMIC_TCPPORT) ,CMD_QUERY_DYNAMIC_TCPPORT);

	if ( NULL != (ptr = _popen(pChBuf, "r")) )
	{
		while (NULL != fgets(buf_ps, sizeof(buf_ps), ptr) )
		{
			strcat_s(pChRet, _countof(buf_ps), buf_ps);
			if (strlen(pChRet) > CMD_RESULT_BUF_SIZE)
			{
				iRet = -2;
				break;
			}
		}
		_pclose(ptr);
		ptr = NULL;
		iRet = 0;  // 处理成功
	}
	else
	{
		WriteError(_T("popen %s error\n"), pChBuf);
		iRet = -1; // 处理失败
	}

	return iRet;
}

/*
* @fn			getDynamicPortFromstr
* @brief		从cmd命令的输出中获取动态端口的启动地址和范围
* @param[in]    
* @param[out]	stuDynamicPort: 动态端口的起始地址和范围
* @return		0 表示成功,其他表示失败
*               
* @detail      	该动态端口为TCP端口
* @author		mingming.shi
* @date			2022-1-24
*/
int getDynamicPortFromstr(__out STU_PORT_POOL& stuDynamicPort)
{
	int			iRet	= 0;
	size_t		nStrLen	= 0;
	std::string strCmdRet	= "";
	std::vector<unsigned int> vecNumFromStr;
	char chRet[CMD_RESULT_BUF_SIZE] = { 0 };

	// 1 执行CMD获取其输出,格式如下
	/*
		协议 tcp 动态端口范围
		---------------------------------
		启动端口        : 49152
		端口数          : 16384
	*/
	iRet = executeCMD(chRet);
	if ( NO_ERROR != iRet )
	{
		iRet = -1;
		goto _END_;
	}

	strCmdRet = chRet; // char to string
	if(strCmdRet.empty())
	{
		iRet = -2;
		goto _END_;
	}

	// 2 获取CMD输出中的数字
	nStrLen = strCmdRet.length();
	for (size_t i = 0; i < nStrLen; i++) {
		int CurNum = 0;
		bool flag = false;
		while ( !(strCmdRet[i] >= '0' && strCmdRet[i] <= '9') && i < nStrLen ) // 遍历到数字
		{
			i++;
		}

		while ( (strCmdRet[i] >= '0' && strCmdRet[i] <= '9') && i < nStrLen ) // 遍历到字母
		{
			flag = true;
			CurNum = CurNum * 10 + (strCmdRet[i] - '0');
			i++;
		}

		if (flag)
		{
			vecNumFromStr.push_back(CurNum);
		}
	}
	
	if (STU_MEMBER_NUM != vecNumFromStr.size())
	{
		iRet = -3;
		goto _END_;
	}
	stuDynamicPort.nStartPort	= vecNumFromStr[0];
	stuDynamicPort.nRange		= vecNumFromStr[1];
	
	iRet = 0;
_END_:
	return iRet; //集合大小就是不同整数数量
}

/*
* @fn			getAllTcpConnectionsPort
* @brief		获取已连接端口数
* @param[in]    
* @param[out]	ret:已连接端口的set容器
* @return		0 表示执行成功,其他表示失败
*               
* @detail      	
* @author		mingming.shi
* @date			2022-1-24
*/
int getAllTcpConnectionsPort(__out std::set<UINT>& setRet)
{
	int iRet	= 0;
	int nNum	= 0; // TCP连接的数目
	std::set<UINT> setPort;

	PMIB_TCPTABLE_OWNER_PID pTcpTable(NULL);
	DWORD dwSize(0);
	GetExtendedTcpTable(pTcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0);
	pTcpTable = (MIB_TCPTABLE_OWNER_PID *)new char[dwSize];//重新分配缓冲区

	if ( NO_ERROR != GetExtendedTcpTable(pTcpTable, &dwSize, TRUE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) )
	{
		delete pTcpTable;
		pTcpTable = NULL;
		{	
			iRet = -1;
			goto _END_;
		}
	}

	// TCP连接的数目
	nNum = (int)pTcpTable->dwNumEntries; 

	for (int i = 0; i < nNum; i++)
	{
		setPort.insert(htons((u_short)pTcpTable->table[i].dwLocalPort));
	}

	setRet = setPort;
_END_:
	if (pTcpTable != NULL) 
	{
		free(pTcpTable);
		pTcpTable = NULL;
	}

	return iRet;
}

/*
* @fn			getFreeDynamicPort
* @brief		获取未被占用的动态端口段
* @param[in]    stuDynamicPort:系统当前开放的动态端口范围
				CurSysUsedPort:系统当前已使用端口
* @param[out]	stuFreeStaticPort: 从默认端口开始获取的端口范围

* @return		
*               
* @detail      	
* @author		mingming.shi
* @date			2022-1-24
*/
int getFreeDynamicPort(__in STU_PORT_POOL stuDynamicPort, __in std::set<UINT> CurSysUsedPort, __out std::vector<STU_PORT_POOL>& stuFreeDynamicPort)
{
	int iRet = 0;
	UINT nStart = stuDynamicPort.nStartPort;
	UINT nEnd	= stuDynamicPort.nStartPort + stuDynamicPort.nRange;

	for (UINT nPort = nStart; nPort != nEnd; ++nPort)
	{
		STU_PORT_POOL stuTempPort;
		if ( CurSysUsedPort.end() !=  CurSysUsedPort.find(nPort) ) // 找到了未使用端口
		{
			int nRange = 0;

			// 遍历当前启动端口的端口范围
			while ( CurSysUsedPort.end() !=  CurSysUsedPort.find( nPort + nRange) )
			{
				nRange++;
			}

			stuTempPort.nStartPort	= nPort;
			stuTempPort.nRange		= nRange;
		}

		stuFreeDynamicPort.push_back(stuTempPort);
		if (stuFreeDynamicPort.size() > MAX_PORT_NUM)
		{
			iRet = -1;
			goto _END_;
		}
	}
	
_END_:
	return iRet;
}

/*
* @fn			getFreeStaticPort
* @brief		从默认端口开始获取未被系统使用的静态端口和范围
* @param[in]    stuDynamicPort:系统当前开放的动态端口范围
				CurSysUsedPort:系统当前已使用端口
* @param[out]	stuFreeStaticPort: 返回
* @return		0 表示成功,其他表示失败
*               
* @detail      	
* @author		mingming.shi
* @date			2022-1-24
*/
int getFreeStaticPort(__in STU_PORT_POOL stuDynamicPort, __in std::set<UINT> CurSysUsedPort, __out std::vector<STU_PORT_POOL>& stuFreeStaticPort)
{
	int iRet = -1;
	UINT nStart = DEFAULT_START_PORT;
	UINT nEnd	= DEFAULT_STOP_PORT;

	for (UINT nPort = nStart; nPort != nEnd; ++nPort)
	{
		STU_PORT_POOL stuTempPort;

		if ( CurSysUsedPort.end() == CurSysUsedPort.find(nPort) && false == stuDynamicPort.isContain(nPort) ) // 找到了未使用端口,并且不在动态端口范围内
		{
			int nRange = 0;

			// 遍历当前启动端口的端口范围
			do
			{
				nRange++;
			}while ( 
				CurSysUsedPort.end() ==  CurSysUsedPort.find(nPort + nRange) && // 系统未使用
				false == stuDynamicPort.isContain(nPort + nRange) && // 不在动态端口范围
				nRange < DEFAULT_PORT_RANGE); // 范围小于默认端口范围

			stuTempPort.nStartPort	= nPort;
			stuTempPort.nRange		= nRange >= DEFAULT_MIN_PORTRANGE ? nRange : DEFAULT_MIN_PORTRANGE;
			stuTempPort.bEnable		= true;

			// 更新启动端口并检查合法性
			nPort += nRange; 
			nPort = nPort >= DEFAULT_STOP_PORT ? DEFAULT_START_PORT : nPort;
		}

		stuFreeStaticPort.push_back(stuTempPort);
		if (stuFreeStaticPort.size() >= MAX_PORT_NUM) // 只是数组满了而已,仍然返回成功
		{
			iRet = 0; 
			goto _END_;
		}
	}

	iRet = 0;
_END_:
	return iRet;
}

/*
* @fn			getFreeDynamicPortsNum
* @brief		获取剩余动态端口数量
* @param[in]    
* @param[out]	nNum:系统空闲动态端口数量
* @return		0 表示成功,其他表示失败
*               
* @detail      	
* @author		mingming.shi
* @date			2022-1-24
*/
int getFreeDynamicPortsNum(__out int& nNum)
{
	int iRet = 0;
	int nPortsNum = 0;

	STU_PORT_POOL stuDynamicPort;
	std::set<UINT> CurSysUsedPort;
	std::set<UINT>::iterator it;

	// 1 获取动态端口状态(起始地址和范围)
	iRet = getDynamicPortFromstr(stuDynamicPort);
	if ( NO_ERROR != iRet ) 
	{
		iRet = -1;
		goto _END_;
	}

	// 2 获取当前系统端口占用情况
	iRet = getAllTcpConnectionsPort(CurSysUsedPort);
	if ( NO_ERROR != iRet ) 
	{
		iRet = -2;
		goto _END_;
	}

	// 3 统计动态端口个数
	for (it = CurSysUsedPort.begin(); it != CurSysUsedPort.end(); it++)
	{
		if(true == stuDynamicPort.isContain(*it))
		{
			nPortsNum++;
		}
	}

	nNum = stuDynamicPort.nRange - nPortsNum;
	iRet = 0;

_END_:
	if (CurSysUsedPort.size())
	{
		CurSysUsedPort.clear();
	}
	return iRet;
}

/*
* @fn			updatePort
* @brief		端口池更新:更新一个端口到端口池中
* @param[in]    
* @param[out]	
* @return		
*               
* @detail      	
* @author		mingming.shi
* @date			2022-1-27
*/
void updatePort(__in __out std::vector<STU_PORT_POOL>::iterator it)
{
	UINT nStart = DEFAULT_START_PORT;
	UINT nEnd	= DEFAULT_STOP_PORT;
	STU_PORT_POOL	stuDynamicPort;
	std::set<UINT>	CurSysUsedPort;
	std::vector<STU_PORT_POOL>::reverse_iterator itPortPoolEnd = g_stuPortPool.rbegin();

	// 1 获取动态端口状态(起始地址和范围)
	getDynamicPortFromstr(stuDynamicPort);

	// 2 获取端口占用情况
	getAllTcpConnectionsPort(CurSysUsedPort);

	// 3 更新启动端口,从端口池最后一个端口开始
	if (true == itPortPoolEnd->isLegal())
	{
		nStart = itPortPoolEnd->nStartPort > DEFAULT_START_PORT ? itPortPoolEnd->nStartPort : DEFAULT_START_PORT;
	}
	else
	{
		nStart = DEFAULT_START_PORT;
	}

	// 4 寻找可用端口和范围
	for (UINT nPort = nStart; nPort <= DEFAULT_STOP_PORT; ++nPort)
	{
		STU_PORT_POOL stuTempPort;

		if ( CurSysUsedPort.end() != CurSysUsedPort.find(nPort) && false == stuDynamicPort.isContain(nPort) ) // 找到了未使用端口,并且不再动态端口范围内
		{
			int nRange = 0;

			// 遍历当前启动端口的端口范围
			while ( CurSysUsedPort.end() !=  CurSysUsedPort.find(nPort + nRange) && false == stuDynamicPort.isContain(nPort + nRange) )
			{
				nRange++;
				if (nRange >= DEFAULT_PORT_RANGE)
				{
					break;
				}
			}

			stuTempPort.nStartPort	= nPort;
			stuTempPort.nRange		= nRange;
			stuTempPort.bEnable		= true;

			// 更新启动端口并检查合法性
			nPort += nRange; 
			nPort = nPort >= DEFAULT_STOP_PORT ? DEFAULT_START_PORT : nPort;
		}

		if (true == stuTempPort.isLegal())
		{
			it->nStartPort = stuTempPort.nStartPort;
			it->nRange = stuTempPort.nRange;
			it->bEnable = stuTempPort.bEnable;
		}
		
		if (g_stuPortPool.size() > MAX_PORT_NUM)
		{
			break;
		}
	}
}

/*
* @fn			scanSysDynamicPortsNum
* @brief		扫描系统当前剩余动态端口数量
* @param[in]    
* @param[out]	
* @return		
*               
* @detail      	
* @author		mingming.shi
* @date			2022-1-27
*/
unsigned int WINAPI scanSysDynamicPortsNum(void* lpParam)
{
	int iRet = -1;

	while(1)
	{
		int nDynamicNum = INT_MAX;
		//step1: 执行动态端口扫描流程,获取 启动端口 和 范围
		if ( NO_ERROR != getFreeDynamicPortsNum(nDynamicNum) )
		{
			WriteInfo(_T("LINE [%d] PortPool Get Dynamic Port Number Failed!\n"), __LINE__);
			iRet = -1;
			goto _END_;
		}

		//step2:判断当前可用动态端口是否需要启用端口池
		if (START_STATIC_PORT_FLAG >= nDynamicNum)
		{
			// WriteInfo(_T("LINE [%d] PortPool Start Get Static Port!\n"), __LINE__);
			iRet = createPortPool();
			if (NO_ERROR != iRet)
			{
				WriteInfo(_T("LINE [%d] PortPool Create Static PortPool FAILED!\n"), __LINE__);
				iRet = -2;
				goto _END_;//
			}
			WNTPORT::g_bFlagGetStaticPort = true;
		}
		else
		{
			WNTPORT::g_bFlagGetStaticPort = false;
		}

		//step3: 等待停止线程信号,每30s执行一次
		DWORD dwRet = WaitForSingleObject(WNTPORT::g_hEvent, TIME_SCAN_FREQUENCY);
		switch (dwRet)
		{
			case WAIT_OBJECT_0:
				// hProcess所代表的进程在 TIME_SCAN_FREQUENCY 秒内结束
				iRet = 0;
				WNTPORT::g_bStartThread = false;
				WNTPORT::g_bFlagGetStaticPort = false; // TODO
				WriteInfo(_T("LINE [%d] PortPool killed PortPool SUCCESS!\n"), __LINE__);
				goto _END_;

			case WAIT_TIMEOUT:
				// 等待时间超过5秒,暂不处理
				
				break;

			case WAIT_FAILED:
				// 函数调用失败,比如传递了一个无效的句柄
				WriteError(_T("LINE [%d] PortPool killed PortPool FAILED!\n"), __LINE__);
				break;
		}

		iRet = 0;
	}
	
_END_:
	return iRet;
}

posted @ 2022-02-10 14:18  StimuMing  阅读(168)  评论(0编辑  收藏  举报