#include <iostream>
#include <mutex>
#include <atomic>
using namespace std;
#ifdef WIN32
#include <winsock2.h>
#include <WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/net.h>
#endif
//在服务端经常需要分配端口用来接收其他信息,所以这时就在端口资源中找出可用的端口资源
class CAlloct_port
{
public:
static CAlloct_port * GetInstance();
bool GetAvailableTCPPort(const char* ip, unsigned short& port);
bool GetAvailableUDPPort(const char* ip, unsigned short& port);
bool SetAllocPortMin(const unsigned int& minport);
bool SetAllocPortMax(const unsigned int& maxport);
private:
CAlloct_port();
~CAlloct_port();
CAlloct_port(CAlloct_port&) = delete;
CAlloct_port& operator=(CAlloct_port&) = delete;
private:
static CAlloct_port* m_Instance;
static std::mutex m_InstanceLocker;
unsigned int m_alloc_min;
unsigned int m_alloc_max;
std::atomic<int> m_lastPort; //防止多线程下出问题
};
#include "CAlloct_port.h"
CAlloct_port* CAlloct_port::m_Instance = nullptr;
std::mutex CAlloct_port::m_InstanceLocker;
CAlloct_port * CAlloct_port::GetInstance()
{
if (nullptr == m_Instance)
{
std::lock_guard<std::mutex> l(m_InstanceLocker);
if (nullptr == m_Instance)
{
m_Instance = new (std::nothrow) CAlloct_port();
if (nullptr == m_Instance)
{
std::cout << __FUNCTION__ << ":" << __LINE__ << "实例化失败" << std::endl;
}
}
}
return m_Instance;
}
bool CAlloct_port::GetAvailableTCPPort(const char* ip, unsigned short& port)
{
struct sockaddr_in sSvrAddr;
memset(&sSvrAddr, 0, sizeof(sSvrAddr));
sSvrAddr.sin_family = AF_INET;
//将点分十进制转换为二进制
if (1 != inet_pton(AF_INET, ip, (void*)&sSvrAddr.sin_addr))
{
#ifdef WIN32
printf("inet_pton失败,%d", ::GetLastError());
#else
::perror(inet_pton失败);
#endif
}
//将二进制转换未点分十进制
//char IPdotdec[20] = {};
//if (NULL == inet_ntop(AF_INET, (PVOID*)&sSvrAddr.sin_addr, IPdotdec, 20))
//{
// printf("inet_ntop失败1,%d", ::GetLastError());
//}
//int listen(int sockfd,int backlog);第二个参数是提示内核监听队列的最大长度,监听队列的长度如何超过backlog
//服务端将不在会受理新的连接请求,并且客户端会受到ECONNREFUSED的错误信息
int cycle = 1;
while (1)
{
if (m_lastPort > m_alloc_max)
{
++cycle;
if (cycle >= 2)
{
return false;
}
m_lastPort = m_alloc_min;
}
sSvrAddr.sin_port = htons(m_lastPort);
SOCKET iSvrFd = socket(AF_INET, SOCK_STREAM, 0);
if (0 == ::bind(iSvrFd, (struct sockaddr*)&sSvrAddr, sizeof(sSvrAddr)))
{
//该端口可用
port = m_lastPort;
return true;
}
#ifdef WIN32
printf("错误码为:%d",::GetLastError());
closesocket(iSvrFd);
#else
close(iSvrFd);
#endif
//端口不可用,重新分配
m_lastPort.fetch_add(1);
}
return false;
}
bool CAlloct_port::GetAvailableUDPPort(const char* ip, unsigned short& port)
{
struct sockaddr_in sSvrAddr;
memset(&sSvrAddr, 0, sizeof(sSvrAddr));
sSvrAddr.sin_family = AF_INET;
if (1 != inet_pton(AF_INET, ip, (void*)&sSvrAddr.sin_addr))
{
#ifdef WIN32
printf("inet_pton失败,%d", ::GetLastError());
#else
::perror(inet_pton失败);
#endif
}
//将二进制转换未点分十进制
//char IPdotdec[20] = {};
//if (NULL == inet_ntop(AF_INET, (PVOID*)&sSvrAddr.sin_addr, IPdotdec, 20))
//{
// printf("inet_ntop失败1,%d", ::GetLastError());
//}
int cycle = 1;
while (1)
{
if (m_lastPort > m_alloc_max)
{
++cycle;
if (cycle >= 2)
{
return false;
}
m_lastPort = m_alloc_min;
}
sSvrAddr.sin_port = htons(m_lastPort);
SOCKET iSvrFd = socket(AF_INET, SOCK_DGRAM, 0);
if (0 == ::bind(iSvrFd, (struct sockaddr*)&sSvrAddr, sizeof(sSvrAddr)))
{
//该端口可用
port = m_lastPort;
return true;
}
#ifdef WIN32
printf("错误码为:%d", ::GetLastError());
closesocket(iSvrFd);
#else
close(iSvrFd);
#endif
//端口不可用,重新分配
m_lastPort.fetch_add(1);
}
return false;
}
bool CAlloct_port::SetAllocPortMin(const unsigned int& minport)
{
if (minport < 0 || minport > 65535)
{
return false;
}
m_alloc_min = minport;
m_lastPort = m_alloc_min;
return true;
}
bool CAlloct_port::SetAllocPortMax(const unsigned int& maxport)
{
if (maxport < 0 || maxport > 65535)
{
return false;
}
m_alloc_max = maxport;
return true;
}
CAlloct_port::CAlloct_port()
:m_alloc_min(1024),
m_alloc_max(65535)
{
m_lastPort = m_alloc_min;
m_Instance = nullptr;
#ifdef WIN32
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
#endif
}
CAlloct_port::~CAlloct_port()
{
#ifdef WIN32
WSACleanup();
#endif
}