这里用的两个函数:gethostbyname/gethostbyaddr,GetAdaptersInfo,这里主要通过获取IP看链表的操作
获取所有IP地址的函数,如下:
int CIPInfo::GetIPInfo(const char * szHostName)
{
#define HOST_NAME_MAX 256
char szHost[HOST_NAME_MAX];
HOSTENT* pHost;
//LPSTR szIPAddr = NULL;
char* szIPAddr = NULL;
in_addr addr;
memset(szHost, 0, ARRSIZE(szHost));
if (szHostName)
{
if (isalpha(szHostName[0])) /* host address is a name */
{
#ifdef _DEBUG
TRACE("Calling gethostbyname with %s\n", szHostName);
#endif // _DEBUG
pHost = gethostbyname(szHostName);
}
else if (isdigit(szHostName[0])) /* host address is a digit */
{
#ifdef _DEBUG
TRACE("Calling gethostbyaddr with %s\n", szHostName);
#endif // _DEBUG
addr.s_addr = inet_addr(szHostName);
if (addr.s_addr == INADDR_NONE)
{
#ifdef _DEBUG
TRACE("The IPv4 address entered must be a legal address\n");
#endif // _DEBUG
return -1;
}
else
{
pHost = gethostbyaddr((char *) &addr, 4, AF_INET);
}
}
else
{
TRACE("The IPv4 address entered Error!\n");
return -1;
}
}
else
{
::gethostname(szHost, HOST_NAME_MAX);
pHost = gethostbyname(szHost);
}
if (pHost->h_addrtype == AF_INET)
{
int i = 0;
//char *p = NULL;
while ( (szIPAddr = pHost->h_addr_list[i]) != 0) //Addresses are in network byte order
{
memcpy(&addr.s_addr, szIPAddr, pHost->h_length);
szIPAddr = ::inet_ntoa(addr);
#ifdef _DEBUG
TRACE("\tIPv4 Address #%d: %s\n", i, szIPAddr);
#endif // _DEBUG
if (szIPAddr)
{
PIPInfo ipInfo = GetIPObj(szIPAddr);
if (ipInfo)
{
InsertIPObj(*ipInfo);
}
}
i++;
}
}
else if (pHost->h_addrtype == AF_INET6)
{
#ifdef _DEBUG
TRACE("\tRemotehost is an IPv6 address\n");
#endif // _DEBUG
return -1;
}
return 0;
}
注意:
1)这里我将IP6排除在外了,只使用了IP4
2)还有一个就是这里面其实牵涉到Unicode和ANSI字符集I编码的问题,这里并没有严格遵守此规则
3)这里使用了ip地址和域名的方式,这里并没有涉及,因为此函数有个默认形参
在使用HOSTENT获得的结构中,包含本机ip地址的列表,就是h_addr_list,这里我将其保存在了如下的一个结构链表中
typedef struct IPInfo
{
char ip[IP_ADDR_SIZE];
IPInfo* next;
}IPInfo, *PIPInfo;
这里就需要牵涉堆内存空间的开辟和释放,链表的插入和删除,这里我是在类中写入的函数,所以我将释放放置于析构函数中,
如下内存空间的分配
PIPInfo CIPInfo:: GetIPObj(char* szIP)
{
PIPInfo pIP = (PIPInfo)malloc(sizeof(IPInfo));
if (pIP)
{
memset(pIP->ip, 0, sizeof(pIP->ip));
strcpy_s( pIP->ip, szIP);
pIP->next = NULL;
}
return pIP;
}
先开辟空间,再填充数据,这里为了防止临时变量空间的释放,使用的是内容的复制
内存空间的释放:如下
int CIPInfo::FreeIPObj(PIPInfo pIPObj)
{
if (pIPObj)
{
free(pIPObj);
pIPObj = NULL;
}
return 0;
}
释放空间后,一定要将指针置为NULL,至于为什么请参考:
void * 与 空指针NULL
链表的插入:如下
int CIPInfo::InsertIPObj(IPInfo& pIPObj)
{
PIPInfo pTest = m_ipAddrList;
if (m_ipAddrList == NULL)
{
m_ipAddrList = &pIPObj;
}
else
{
while ( pTest->next != NULL)
{
pTest = pTest->next;
}
pTest->next = &pIPObj;
}
return 0;
}
这里m_ipAddrList是类的成员变量,这里链表没有头节点,也没有头指针或者尾指针,只有一个单向链表。链表为空,直接插入;链表不为空,插入尾部
链表的删除:如下:
int CIPInfo::RemoveIPObj(PIPInfo pIPObj)
{
PIPInfo pTest = m_ipAddrList;
if (pIPObj == NULL)
{
return -1;
}
else if (pIPObj == pTest)
{
m_ipAddrList = (pTest->next) ? pTest->next : NULL;
}
else
{
while ( (pTest->next != NULL) &&
(pTest->next != pIPObj) )
{
pTest = pTest->next;
}
if (pTest != NULL)
{
pTest->next = (pIPObj->next) ? pIPObj->next : NULL;
}
}
return 0;
}
这里要考虑链表为空的情况,通常移除的时候就会判断,这里只是保险起见,如果非空,要考虑删除的是否是头结点,是否是仅有一个结点,当时其他元素时,要考虑删除的是否是尾结点,以及在中间的结点。
int CIPInfo::GetMac(void)
{
//PIP_ADAPTER_INFO pAdapterInfo = NULL;
ULONG uLen = 0;
/* 获得内存空间大小,然后申请内存空间*/
if (GetAdaptersInfo(pAdapterInfo, &uLen) == ERROR_BUFFER_OVERFLOW)
{
pAdapterInfo = (PIP_ADAPTER_INFO)::GlobalAlloc(GPTR, uLen); //分配一块内容,并初始化为0
if (pAdapterInfo == NULL)
{
TRACE("Error allocating memory needed to call GetAdaptersinfo\n");
return -1;
}
}
/* 取得适配器信息*/
if ( ::GetAdaptersInfo(pAdapterInfo, &uLen) == ERROR_SUCCESS )
{
if (pAdapterInfo)
{
memcpy(m_szLocalMac, pAdapterInfo->Address, MAC_ADDR_SIZE);
strcpy_s(m_szLocalIP, pAdapterInfo->IpAddressList.IpAddress.String);
strcpy_s(m_szMask, pAdapterInfo->IpAddressList.IpMask.String);
strcpy_s(m_szGatewayIP, pAdapterInfo->GatewayList.IpAddress.String);
}
}
return 0;
}
另外千万不要忘了内存空间的释放,因为不释放分配的空间,会导致内存泄露,如下析构函数
CIPInfo::~CIPInfo(void)
{
PIPInfo pTest = m_ipAddrList;
while (pTest)
{
RemoveIPObj(pTest);
FreeIPObj(pTest);
pTest = m_ipAddrList->next;
}
m_ipAddrList = NULL;
if (pAdapterInfo)
GlobalFree(pAdapterInfo);
}
测试用例:这里我使用了MFC的主窗口类,在一个编辑框中进行显示,如下测试代码:
void CSocketDlg::OnBnClickedOk()
{
// TODO: 在此添加控件通知处理程序代码
CString strText = _T("");
CString strTemp = _T("");
CIPInfo ipInfo;
int nIndex = -1;
nIndex = m_comboBox.GetCurSel();
m_comboBox.GetLBText(nIndex, strText);
if (strText == _T("主机IP地址"))
{
strText += _T("\r\n");
PIPInfo IPInfoList;
int nResult = -1;
nResult = ipInfo.GetIPInfo();
if( !nResult )
{
IPInfoList = ipInfo.GetIPList();
while ( IPInfoList &&
(strTemp = (CString)IPInfoList->ip))
{
strTemp += _T("\r\n");
strText += strTemp;
IPInfoList = IPInfoList->next;
}
}
}
if (strText == _T("主机MAC地址"))
{
strText += _T("\r\n");
ipInfo.GetMac();
strTemp.Format(_T("IP\t: %s\r\n"), (CString)ipInfo.GetLocalIP());
strText += strTemp;
strTemp.Format(_T("Mask\t: %s\r\n"), (CString)ipInfo.GetMask());
strText += strTemp;
strTemp.Format(_T("Gateway\t: %s\r\n"),(CString)ipInfo.GetGatewayIP());
strText += strTemp;
u_char* p = ipInfo.GetLocalMac();
strTemp.Format(_T("MAC\t: %.2X-%.2X-%.2X-%.2X-%.2X-%.2X"),
p[0],p[1],p[2],p[3],p[4],p[5]);
strText += strTemp;
}
SetDlgItemText(IDC_SHOW_TEXT, strText);
//CDialogEx::OnOK();
}
测试IP的结果如下:
主机IP地址
192.168.1.3
192.168.50.1
192.168.159.1
测试MAC配置信息的结果如下:
主机MAC地址
IP : 192.168.1.3
Mask : 255.255.255.0
Gateway : 192.168.1.1
MAC : 9C-4E-36-17-AA-A4