Windows网络编程技术[一]

四 25th, 2010 | Filed under Network

作者:Figo

从今天起,我们开始Windows平台下网络编程的学习。本篇主要讲解网络基础知识和本地主机网络相关信息查看涉及的API使用:
IP View

在开始进入后面的实际编程之前,首先需要掌握一些网络基本知识:如TCP/IP协议中网络分为层,应用层协议都有哪些等等。因为时间关系,我不可能巨细无遗的把所有知识都说明,所以更多的还要靠自己去Google看书

在Windows中,获取和设定本地网络配置信息的API集是IP Helper,下面我以GetIfTable函数为例讲解它们的用法,其他函数的使用大同小异。

//...figoyao.com
//适配器信息结构定义
typedef struct _FADAPTER_INFO
{
	//接口列表次序
	DWORD dwOrder;

	//接口内部索引号
	DWORD dwIndex;
	//接口名称描述
	char szName[MAX_INTERFACE_NAME_LEN];
	//接口类型[本地回路/无线网卡/以太网卡/]
	char szAdapterType[64];
	//接口速度[Mbps为单位]
	DWORD dwSpeed_Mbps;

	//物理地址FF-FF-FF-FF-FF-FF
	char szMAC_Addr[32];
	//连接状态信息
	char szOperStatus[64];

	//发送和接收数据[bytes]
	DWORD dwOUTData;
	DWORD dwINData;

	//IP地址和子网掩码
	char szIP[32];
	char szSubMask[32];

}FADAPTER_INFO,*PFADAPTER_INFO;

下面的代码片段是获取适配器信息过程:

//...figoyao.com
//获取网卡详细信息
				PMIB_IFTABLE pstIfTable = NULL;
				ULONG ulIfTable = 0;
				GetIfTable(NULL,&ulIfTable,TRUE);
				pstIfTable = (PMIB_IFTABLE)MALLOC(ulIfTable);
				if (NULL == pstIfTable)
				{
					MessageBox(hwnd,"(PMIB_IFTABLE)MALLOC(ulIfTable)失败","错误",MB_OK|MB_ICONERROR);
					SendMessage(hwnd,WM_CLOSE,0,0);
				}
				DWORD dwGetIfTableRet = GetIfTable(pstIfTable,&ulIfTable,TRUE);
				if (NO_ERROR == dwGetIfTableRet)
				{
					if (pstIfTable->dwNumEntries != dwNumOfMac)
					{
						MessageBox(hwnd,"请报告你的Windows系统版本","错误",MB_OK|MB_ICONERROR);
						SendMessage(hwnd,WM_CLOSE,0,0);
					}
					for (DWORD dwCnt = 0;dwCnt < pstIfTable->dwNumEntries;dwCnt++)
					{
						//索引号赋值[以0为起始]
						pstAdapterInfo[dwCnt]->dwOrder = dwCnt;
						pstAdapterInfo[dwCnt]->dwIndex = pstIfTable->table[dwCnt].dwIndex;
						pstAdapterInfo[dwCnt]->dwSpeed_Mbps = pstIfTable->table[dwCnt].dwSpeed/1000000;
						switch(pstIfTable->table[dwCnt].dwType)
						{
							//以太网适配器
							case IF_TYPE_ETHERNET_CSMACD:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"以太网适配器");
							}
							break;

							//令牌环适配器
							case IF_TYPE_ISO88025_TOKENRING:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"令牌环适配器");
							}
							break;

							//点到点协议适配器
							case IF_TYPE_PPP:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"点到点协议适配器");
							}
							break;

							//An IEEE 802.11 wireless network interface
						case IF_TYPE_IEEE80211:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"IEEE 802.11无线网络适配器");
							}
							break;

							//A tunnel type encapsulation network interface.
						case IF_TYPE_TUNNEL:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"管道网络适配器");
							}
							break;

							//An ATM network interface.
						case IF_TYPE_ATM:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"ATM网络适配器");
							}
							break;

							//A software loopback network interface.
						case IF_TYPE_SOFTWARE_LOOPBACK:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"软件回路适配器");
							}
							break;

							//An IEEE 1394 (Firewire) high performance serial bus network interface.
						case IF_TYPE_IEEE1394:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"IEEE 1394高性能串口适配器");
							}
							break;

						//光纤接口适配器
						case MIB_IF_TYPE_FDDI:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"光纤接口适配器");
							}
							break;

							//串行适配器(Serial Line Interface Protocol)
						case MIB_IF_TYPE_SLIP:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"串行适配器(Serial Line Interface Protocol)");
							}
							break;

						default:
							{
								sprintf(pstAdapterInfo[dwCnt]->szAdapterType,"其他类型适配器");
							}
							break;
						}

						//接口描述
						sprintf(pstAdapterInfo[dwCnt]->szName,"%d->%s",dwCnt+1,pstIfTable->table[dwCnt].bDescr);
						SendMessage(GetDlgItem(hwnd,IDC_CMB_ADAPTER),CB_ADDSTRING,0,(LPARAM)pstAdapterInfo[dwCnt]->szName);
						//物理地址
						if (0 == pstIfTable->table[dwCnt].dwPhysAddrLen)
						{
							sprintf(pstAdapterInfo[dwCnt]->szMAC_Addr,"00-00-00-00-00-00");
						}
						else
						{
							for (int j = 0;j < pstIfTable->table[dwCnt].dwPhysAddrLen;j++)
							{
								char szMACStr[6] = "";
								if (j != pstIfTable->table[dwCnt].dwPhysAddrLen - 1)
								{
									sprintf(szMACStr,"%02x-",pstIfTable->table[dwCnt].bPhysAddr[j]);
								}
								else
								{
									sprintf(szMACStr,"%02x",pstIfTable->table[dwCnt].bPhysAddr[j]);
								}
								strcat(pstAdapterInfo[dwCnt]->szMAC_Addr,szMACStr);
							}
						}
						//收发数据[bytes]
						pstAdapterInfo[dwCnt]->dwINData = pstIfTable->table[dwCnt].dwInOctets;
						pstAdapterInfo[dwCnt]->dwOUTData = pstIfTable->table[dwCnt].dwOutOctets;
					}
				}
				else
				{
					char szTmpGetIfTableRet[32] = "";
					sprintf(szTmpGetIfTableRet,"[错误代码:%d]GetIfTable失败",dwGetIfTableRet);
					MessageBox(hwnd,szTmpGetIfTableRet,"错误",MB_OK|MB_ICONERROR);
					SendMessage(hwnd,WM_CLOSE,0,0);
				}
			}
			else
			{
				MessageBox(hwnd,"适配器接口数量获取失败","错误",MB_OK|MB_ICONERROR);
				SendMessage(hwnd,WM_CLOSE,0,0);
			}
//.............

外网IP获取部分,这里的方法是通过访问一个脚本获取,通过http://figoyao.com/pip.php即可获取客户端IP地址,在这个地址失败后,尝试http://figo.oni.cc/pip.php,如果依旧失败则不再尝试:

//...figoyao.com
/********************************************
*函数功能:
*获取本地主机在外网的IP地址
*入口参数:
*_out szPIP 接收外网IP的字符串地址
*
*返回值:
*成功:TRUE
*失败:FALSE
*
*注解:
*无
********************************************/
BOOL GetWANIPAddr(char *szPIP)
{
	//1.1.1.1
	if (NULL == szPIP)
	{
		return FALSE;
	}

	//检测网络状态
	char szUrl[] = "http://www.baidu.com";
	BOOL bState = InternetCheckConnection(szUrl,FLAG_ICC_FORCE_CONNECTION,0);
	if (FALSE == bState)
	{
		return FALSE;
	}

	//获取公网IP的网址及备份网址
	char szPIPUrl_Bak[] = "http://figoyao.com/pip.php";
	char szPIPUrl[] = "http://figo.oni.cc/pip.php";

	char szUserAgent[] = "figoyao/10.25 (Windows; U; Windows NT 6.4; zh-CN; rv:1.8.9) China/20121222";
	//创建会话
	HINTERNET hSession = InternetOpen(szUserAgent,INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0);
	if (NULL == hSession)
	{
		return FALSE;
	}

	HINTERNET hOpenUrl = NULL;
	char szRequesHeader[] = "Accept: text/html\r\n";

	hOpenUrl = InternetOpenUrl(hSession,szPIPUrl,szRequesHeader,strlen(szRequesHeader),\
		INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_PRAGMA_NOCACHE|INTERNET_FLAG_RELOAD,0);

	if (NULL == hOpenUrl)
	{
		//如果打开第一个网址失败则尝试通过备份网址获取公网IP
		hOpenUrl = InternetOpenUrl(hSession,szPIPUrl_Bak,szRequesHeader,strlen(szRequesHeader),\
			INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_PRAGMA_NOCACHE|INTERNET_FLAG_RELOAD,0);
		if (NULL == hOpenUrl)
		{
			if (hSession)
			{
				InternetCloseHandle(hSession);
			}
			return FALSE;
		}
		goto getpip;
	}

getpip:
	{
		//一次调用从服务器读取数据
		DWORD dwNumOfBufRead = 0;
		char lpGetIPBuf[32] = "";
		BOOL bReadFile = InternetReadFile(hOpenUrl,lpGetIPBuf,32,&dwNumOfBufRead);

		//检测读取的地址是否符合要求
		if (dwNumOfBufRead > 32 || FALSE == bReadFile)
		{
			if (hSession)
			{
				InternetCloseHandle(hSession);
			}
			if (hOpenUrl)
			{
				InternetCloseHandle(hOpenUrl);
			}
			return FALSE;
		}

	//	memset(szPIP,0,sizeof(szPIP));
		DWORD dwTmpIPLen = strlen(lpGetIPBuf);
		//接收的数据是以0+\r\n\r\n结尾,故减去5
		strncpy(szPIP,lpGetIPBuf,dwTmpIPLen-5);
	}

	if (hSession)
	{
		InternetCloseHandle(hSession);
	}
	if (hOpenUrl)
	{
		InternetCloseHandle(hOpenUrl);
	}
	return TRUE;
}

程序在Windows7测试未通过,但是控制台版本的没问题,究竟是何原因暂时没有深究。这次是使用VS2003编译的

源码:点击下载

转载请注明出处:http://www.figoyao.com/blog/2010/04/25/1352

  1. kent
    四 25th, 201022:09

    Figo 带头大哥的更新很迅速呀!我还在温习前面的内容。