您的位置:首页 > 理论基础 > 计算机网络

Windows网络编程之Winsock 编程接口实验

2017-04-08 13:25 831 查看

实验一 Winsock 编程接口实验

1 实验类型

验证型实验

2 实验目的

1. 掌握Winsock 的启动和初始化;

2. 掌握gethostname(),gethostbyname(),GetAdaptersInfo()等信息查询函数的使用。

3 背景知识

(1)Winsock 基本概念

Winsock 即Windows Sockets 的简称,是在Windows 操作系统平台下使用的套接字接口。

自Winsock2 规范开始,Windows Sockets 就成为了一个得到广泛应用的、开放的、支持多种

协议的网络编程接口。它是一个与协议无关的传送接口,可以用该接口来调用大多数网络协

议,而不需要知道这些协议具体的实现细节。

Win32 平台下的Winsock 编程接口支持的协议包括:IP、IPX/SPX、NetBIOS、AppleTalk、

ATM 等,这些协议具有相当大的差异,例如它们支持不同范围的网络、不同的寻址方式、

不同的传输介质等,但它们的调用操作大多数是相同的。因此,定义统一的Winsock 编程接

口,有利于实现采用统一的方法对实现对不同协议的调用,简化网络程序设计过程。

Winsock编程接口的实质是一套AP(I Application Programming Interface,应用编程接口),

称为Winsock API,由一系列函数构成。在编写网络程序的时候,经常需要跟这些函数打交

道,通过对这些函数的调用,实现对具体网络协议的控制,达到实现网络事务的目的。

(2)Winsock 的启动和终止

由于Winsock 的服务是以动态链接库Winsock DLL(在Windows 安装目录下的system32

下,假如Windows 安装在C:\WINNT 目录下,则在C:\WINNT\ system32 目录下可以找到

winsock.dll)形式实现的,因此必须先调用WSAStartup 函数实现对Winsock DLL 的初始化,

协商Winsock 的版本,并分配必要的资源。

WSAStartup 函数原型为:

int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);

其中,参数wVersionRequested 用于指定准备加载的Winsock 库的版本;通常的做法是

高位字节指定所需要的Winsock 库的副版本,低位字节指定所需要的Winsock 库的主版本,

后,再用宏MAKEWORD(X,Y)(X 是低位字节,Y 是高位字节)获得wVersionRequested

的正确值。

lpWSAData 参数是指向LPWSADATA 结构的指针,该结构包含了加载的库版本的有关

信息,具体格式如下:

Typedef struct WSAData{

WORD wVersion;

WORD wHighVersion;

char szDescription[WSADESCRIPTION_LEN+1];

char szSystemStatus[WSASYS_STATUS_LEN+1];

unsigned short iMaxSockets;

unsigned short iMaxUdpDg;

char FAR * lpVendorInfo;

}WSADATA, *LPWSADATA;

其中,wVersion 字段为希望使用的Winsock 版本。字段wHighVersion 返回现有Winsock

库的最高版本。szDescription 和szSystemStatus 这两个字段由特定的Winsock 实施方案设定,

事实上没有使用。字段iMaxSockets 和iMaxUdpDg 分别为可同时打开的套接字数和数据报

的最大长度,一般不要使用它们,若想知道数据报的最大长度则应该通过WSAEnumProtocols

函数来查询协议信息,而可同时打开套接字的最大数目是不固定的,它很大程度上和可用物

理内存的数量有关。最后一个字段lpVendorInfo 是为厂商预留的,任何一个Win32 平台都没

有使用这个字段。

此外,在应用程序关闭套接字后,还应调用WSACleanup 函数终止对Winsock DLL 的使

用,并释放资源,以备下次使用。

WSACleanup 函数的原型如下:

int WSACleanup(void);

该函数不带任何参数,若调用成功则返回0,否则返回错误。

(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;

4 实验内容

1、编写程序能同时实现对多个域名的解析。比如在控制台输入:getip www.163.com

www.swust.edu.cn, 能输出www.163.com 和 www.swust.edu.cn 对应的IP 地址列表。

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

下面是代码部分:


1.第一题代码:

#include<stdio.h>
#include<WINSOCK2.H>
#include<string.h>
#pragma comment(lib,"WS2_32.lib")
int main(){
char hostIp[300];  //取得主机名称
WSADATA WSAData;
if(WSAStartup(MAKEWORD(2,2),&WSAData)!=0){
exit(1);
}                   //初始化

printf("请输入要解析的域名:");

gets(hostIp);
hostent *pHost=gethostbyname(hostIp);

if(pHost==NULL){
printf("少年,你确定你输入的域名是正确的???\n"); //若输入错误的域名,则程序结束并提示
return -1;
}
in_addr addr; //in_addr是一个结构体,用来保存网络字节顺序格式的IP地址
for(int i=0;;i++){         //循环的作用是,有可能一个域名对应多个IP地址
ch
4000
ar *p=pHost->h_addr_list[i];
if(p==NULL){
break;
} //当解析完成,程序退出
memcpy(&addr.S_un.S_addr,p,pHost->h_length);//内存拷贝函数,将p内存地址的起始位置拷贝pHost->h_length个字节到源地址&addr.S_un.S_addr中去
char *addIp=inet_ntoa(addr);//将网络地址转换为点分十进制的IP地址格式
printf("\n域名:%s\nIP地址:%s\n",hostIp,addIp);
printf("服务器名字:%s\n",pHost->h_name);
return 0;

}
WSACleanup();

}
/*  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;
};*/


运行结果:



第二题代码:

// GetIPConfig.cpp : 定义控制台应用程序的入口点。
//

//#include "stdafx.h"
#pragma comment(lib, "IPHLPAPI.lib")
#include <winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>

int main(int argc, char* 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;
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息