实验一 Winsock编程接口实验

一、实验目的

        掌握 Winsock 的启动和初始化;
        掌握 gethostname(),gethostbyname(),GetAdaptersInfo()等信息查询函数的使用;
        编写程序能同时实现对多个域名的解析;
        编写程序获取并输出本地主机的所有适配器的 IP 地址,子网掩码,默认网关,MAC地址。

二、实验设计

        该实验分为两个部分:编写程序能同时实现对多个域名的解析和编写程序获取并输出本地主机的所有适配器的 IP 地址,子网掩码,默认网关,MAC 地址。
        1. 编写程序能同时实现对多个域名的解析。
        步骤:

(1)完成Socket的启动和初始化;

(2)使用gethostname()获取主机名称;

(3)使用gethostbyname()进行域名解析;

        2. 编写程序获取并输出本地主机的所有适配器的 IP 地址,子网掩码,默认网关,MAC 地址。
        步骤:

(1)第1次调用GetAdaptersInfo(),获取返回结果的大小到ulOutBufLen中;

(2)第2次调用GetAdaptersInfo(),获取本地网络信息到结构体pAdapterInfo中;

(3)从pAdapterInfo 获取并显示本地网络信息;

(4)释放资源。

        3. 函数原型
        (1)gethostname()
        函数原型为:

int gethostname(char *name, int namelen);

name 是一个指向将要存放主机名的缓冲区指针。

namelen 用于指定缓冲区的长度。

        该函数把本地主机名存入由 name 参数指定的缓冲区中,返回的主机名是一个以NULL 结束的字符串。
        (2)gethostbyname()
        函数原型为:

struct hostent *gethostbyname(const char *name);

name 为指向主机名的指针,它一般由函数 gethostname 返回。

        函数返回对应于给定主机名的包含主机名字和地址信息的 hostent 结构指针,该结构格式如下:

struct hostent{
char FAR* h_name;
char FAR* FAR* h_aliases;
short  h_addrtype;
short  h_length;
char FAR* FAR* h_addr_list;
};

        (3)DWORD GetAdaptersInfo()
        该函数可以获取本地主机的所有适配器信息,并保存在 pAdapterInfo 所指向的链表中。函数原型为:

DWORD GetAdaptersInfo(
PIP_ADAPTER_INFO pAdapterInfo, // buffer to receive data
PULONG pOutBufLen // size of data returned
);
IP_ADAPTER_INFO 的定义如下:
typedef struct _IP_ADAPTER_INFO {
struct _IP_ADAPTER_INFO* Next;
DWORD ComboIndex;
char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4];
char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];
UINT AddressLength;
BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH];
DWORD Index;
UINT Type;
UINT DhcpEnabled;
PIP_ADDR_STRING CurrentIpAddress;
IP_ADDR_STRING IpAddressList;
IP_ADDR_STRING GatewayList;
IP_ADDR_STRING DhcpServer;
BOOL HaveWins;
IP_ADDR_STRING PrimaryWinsServer;
IP_ADDR_STRING SecondaryWinsServer;
time_t LeaseObtained;
time_t LeaseExpires;
} IP_ADAPTER_INFO, *PIP_ADAPTER_INFO;

三、实验过程(包含实验结果)

        1.域名解析。

实验一 Winsock编程接口实验_第1张图片

        已经完成启动初始化和获取主机的名称。

实验一 Winsock编程接口实验_第2张图片

        输入要解析的域名:www.163.com 获得解析结果。

实验一 Winsock编程接口实验_第3张图片

        再次解析地址:www.swust.edu.cn

        2. 获取并输出本地主机的所有适配器的 IP 地址,子网掩码,默认网关,MAC 地址。

实验一 Winsock编程接口实验_第4张图片

        运行程序直接显示出需要的结果。

四、讨论与分析

        1.Winsock初始化的作用是什么?
        每个Winsock应用程序都必须加载 Winsock DLL 的相应版本。如果调用Winsock之前没有加载Winsock库,函数会返回 SOCKET_ERROR, 错误信息是 WSANOTINITIALISED。加载Winsock库是通过调用WSAStarup函数实现的。
        2.给出 GetAdaptersInfo()函数的正确使用方法。
        该函数用于获取本地主机的所有适配器信息,并保存在pAdapterInfo所指向的链表中。函数原型为: 
       
 DWORD GetAdaptersInfo( 
            PIP_ADAPTER_INFO pAdapterInfo,       // buffer to receive data               
            PULONG pOutBufLen                         // size of data returned 
        ); 

        第一个参数pAdapterInfo是指向一个缓冲区指针,其中使用IP_ADAPTER_INFO结构体保存获取到的网络适配器信息;第二个参数pOutBufLen是一个指向ULONG变量的指针,保存pAdapterInfo缓冲区的大小。GetAdaptersInfo()函数的返回值为DWORD类型,如果函数调用成功,则返回ERROR_SUCCESS;否则将返回错误编码。
        3.域名解析时出现域名对应多个 IP,请解释原因。
        一个域名用dns解析到多台服务器,服务器如果做了负载冗余,那么其中一个ip的服务器宕机了的话,另外一个立马补上而用户去察觉不到。起到的作用是在你主备服务器的主机宕机之后,立刻启动备机防止应用不能访问,提供24小时不间断服务。所以在域名解析时可能得到几个不同的IP地址。

五、总结

        这次实验过程中主要涉及到socket的启动初始化,然后调用gethostname()获得主机名称。调用gethostbyname()可以去解析输入的域名地址,同一个域名可能解析出不同的IP地址,这是因为很多网站有多个服务器。例如百度这样的大公司肯定有许多备用的服务器。使用GetAdaptersInfo()获得适配器的 IP 地址,子网掩码,默认网关,MAC 地址等等。在域名解析的过程中,我还发现,断开网络将不能解析IP地址,于是我总结出gethostbyname()的原理是将域名发送到域名解析器上解析之后将返回的结果显示出来。

六、附录:关键代码

1.域名解析

char host[256];
	while (printf("请输入要解析的域名:\n"))
	{
		scanf("%s", &host);
		hostent *pHost = ::gethostbyname(host);
		// 打印出所有IP地址
		in_addr addr;
		for (int i = 0;; i++)
		{
			char *p = pHost->h_addr_list[i];//逐个访问域名列表中的地址
			if (p == NULL)
			{
				break;
			}
			memcpy(&addr.S_un.S_addr, p, pHost->h_length);//将地址拷贝到addr之中
			char *szIp = ::inet_ntoa(addr);//转化为点分十进制
			printf("域名%s的IP地址: %s\n", host, szIp);
		}
		printf("\n");
	}

2.获取适配器相关信息

#include "stdafx.h"
#pragma comment(lib, "IPHLPAPI.lib") 
#include  
#include  
#include  
int _tmain(int argc, _TCHAR* argv[])
{
	// 指定获取到的网络信息结构体链表的指针
	IP_ADAPTER_INFO *pAdapterInfo;
	// 保存获取到的网络信息结构体链表的长度
	ULONG  ulOutBufLen;
	// 返回调用编码
	DWORD dwRetVal;
	// 在轮循所有网络适配器信息时使用的单个结构体变量
	PIP_ADAPTER_INFO pAdapter;
	// 为pAdapterInfo分配空间
	pAdapterInfo = (IP_ADAPTER_INFO *)
		malloc(sizeof(IP_ADAPTER_INFO));
	ulOutBufLen = sizeof(IP_ADAPTER_INFO);
	// 第1次调用GetAdaptersInfo(),获取返回结果的大小到ulOutBufLen中
	int err = GetAdaptersInfo(pAdapterInfo,
		&ulOutBufLen);
	if (err != ERROR_SUCCESS)
	{
		printf("error one\n");
		free(pAdapterInfo);
		pAdapterInfo = (IP_ADAPTER_INFO *)
			malloc(ulOutBufLen);
	}
	// 第2次调用GetAdaptersInfo(),获取本地网络信息到结构体pAdapterInfo中
	if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) != ERROR_SUCCESS)
	{
		printf("GetAdaptersInfo Error! %d\n", dwRetVal);
	}
	// 从pAdapterInfo 获取并显示本地网络信息
	pAdapter = pAdapterInfo;
	while (pAdapter)
	{
		printf("网络适配器名: \t\t%s\n",
			pAdapter->AdapterName);
		printf("网络适配器描述: \t%s\n\n",
			pAdapter->Description);
		printf("MAC地址: \t\t");
		for (int i = 0; i<(int)pAdapter->AddressLength; i++)
		{
			if (i == (int)(pAdapter->AddressLength - 1))
				printf("%.2X\n", (int)pAdapter->Address[i]);
			else
				printf("%.2X-", (int)pAdapter->Address[i]);
		}
		printf("IP地址: \t\t%s\n",
			pAdapter->IpAddressList.IpAddress.String);
		printf("子网掩码: \t\t%s\n",
			pAdapter->IpAddressList.IpMask.String);
		printf("网关: \t\t\t%s\n",
			pAdapter->GatewayList.IpAddress.String);
	printf("********************************************************************\n");
		if (pAdapter->DhcpEnabled)
		{
			printf("启用DHCP: \t\t是\n");
			printf("DHCP服务器: \t\t%s\n", pAdapter->DhcpServer.IpAddress.String);
		}
		else
		{
			printf("启用DHCP: \t\t否\n");
		}
		// 处理下一个网络适配器
		pAdapter = pAdapter->Next;
	}
	// 释放资源
	if (pAdapterInfo)
		free(pAdapterInfo);
	printf("\n\n");
	system("pause");
	return 0;
}

        注:本博客源代码下载地址:http://download.csdn.net/detail/dmxexcalibur/9904513

你可能感兴趣的:(Windows网络编程)