获取空闲端口号、判断是否在同一网段

c++

Posted by YiMiTuMi on April 25, 2021

获取空闲端口号

当我们使用端口时要获取空闲的端口,端口数量最大为65536个。

通过Bind函数获取

SOCKET s = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(usPort);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(s,(LPSOCKADDR)&addr,sizeof(addr));
if(WSAGetLastError() == WSAEADDRINUSE)
{
	//端口已被占用
	usPort++;
}
else
{
	//将端口释放掉
	closesocket(s);
	break;
}

不推荐使用,有些情获取不到。

通过TCP连接表和UDP连接表获取

判断当前端口是否空闲时,要同时判断TCP连接表和UDP连接表,使用函数GetTcpTableGetUdpTable获取两个表的信息,进行解析。

#include <tcpmib.h>
#include <IPHlpApi.h>
#include <WinSock2.h>

#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"Iphlpapi.lib")

//获取Tcp端口状态
BOOL TCPPort(ULONG nPort)
{	
	
	MIB_TCPTABLE* TcpTable = NULL;
	DWORD nSize = 0;
	
	//获取表的大小,分配内存
	if (GetTcpTable(TcpTable, &nSize, TRUE) == ERROR_INSUFFICIENT_BUFFER)
	{
		TcpTable = (MIB_TCPTABLE*) malloc ((UINT) nSize);
	}

	if(NO_ERROR == GetTcpTable(TcpTable, &nSize, TRUE))
	{
		DWORD nCount = (*TcpTable).dwNumEntries;
		if (nCount > 0)
		{
			for(DWORD i=0;i<nCount;i++)
			{ 
				MIB_TCPROW TcpRow = (*TcpTable).table[i];
				DWORD temp1 = TcpRow.dwLocalPort;
				int temp2 = temp1 / 256 + (temp1 % 256) * 256;
				if(temp2 == nPort)
				{
					if (TcpTable != NULL)
					{
						delete TcpTable;
					}
					return TRUE;
				}
			}
		}

		if (TcpTable != NULL)
		{
			delete TcpTable;
		}
		return FALSE;
	}

	if (TcpTable != NULL)
	{
		delete TcpTable;
	}
	return FALSE;
}

//获取Udp端口状态
BOOL UDPPort (ULONG nPort)
{
	MIB_UDPTABLE* UdpTable = NULL;
	DWORD nSize = 0;

	if (GetUdpTable(UdpTable, &nSize, TRUE) == ERROR_INSUFFICIENT_BUFFER)
	{
		UdpTable = (MIB_UDPTABLE*) malloc ((UINT) nSize);
	}

	if(NO_ERROR == GetUdpTable(UdpTable, &nSize,TRUE))
	{
		DWORD nCount = (*UdpTable).dwNumEntries;
		if (nCount > 0)
		{
			for(DWORD i=0;i<nCount;i++)
			{
				MIB_UDPROW TcpRow = (*UdpTable).table[i];
				DWORD temp1 = TcpRow.dwLocalPort;
				int temp2 = temp1 / 256 + (temp1 % 256) * 256;
				if(temp2 == nPort)
				{
					if (UdpTable != NULL)
					{
						delete UdpTable;
					}
					return TRUE;
				}
			}
		}

		if (UdpTable != NULL)
		{
			delete UdpTable;
		}
		return FALSE;
	}

	if (UdpTable != NULL)
	{
		delete UdpTable;
	}
	return FALSE;
}


int GetFreePort()
{
	unsigned short usPort = 20000;

	while(TRUE)
	{

		if (!TCPPort(usPort))
		{
			if (!UDPPort(usPort))
			{
				break;
			}
		}

		usPort++;
	}

	return usPort;
}

判断是否在同一网段

判断是否在同一网段也就是判断能否 connect 成功,但是 connect 自己设定的超时时间太长不方便使用,通过设置非阻塞模式,使用 select 函数来判断是否 connect 成功,select 一般用来判断可读写性,但是 select 函数有个返回时间所以可以设定超时时间,所以当 select 返回时可以判断是否连接。

BOOL JudgeIPAndProtConnect(const string &strIp, int &iPort) //接受一个IP和端口
{
	BOOL bJub = TRUE;
	SOCKET ConnectSocket;
	ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (ConnectSocket == INVALID_SOCKET)
	{
		return FALSE;
	}

	do 
	{
		//socket设置为非阻塞 
		unsigned long on = 1;
		if (ioctlsocket(ConnectSocket, FIONBIO, &on) < 0) 
		{
			bJub = FALSE;
			break;
		}


		sockaddr_in clientService; 
		clientService.sin_family = AF_INET;
		clientService.sin_port = htons(iPort);
		clientService.sin_addr.s_addr = inet_addr(strIp.c_str());


		if (connect(ConnectSocket, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR)
		{			
			bJub = FALSE;
			break;
		}
	
		//设置时间
		fd_set writeset;
		FD_ZERO(&writeset);
		FD_SET(ConnectSocket, &writeset);
		timeval tv;
		tv.tv_sec = 2;   //秒
		tv.tv_usec = 0;  //毫秒

		int ret = select(ConnectSocket + 1, NULL, &writeset, NULL, &tv);
		if (ret == 0)  
		{
			bJub = FALSE;

		} else if (ret < 0) 
		{
			bJub = FALSE;
		} 

	} while (FALSE);

	closesocket(ConnectSocket);

	return bJub;
}

蓝色妖姬 – 相守