网络编程实验1_(9)获取本机的IP地址(C++代码)
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<iostream>
#include<WinSock2.h>
#include<string>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
WSADATA wsaData; // WSADATA结构包含有关Windows套接字实现的信息。
WORD wVersionRequest; // wVersionRequested 为 WinSock 规范的版本号,低字节为主版本号,高字节为副版本号(修正版本号)
int iResult; // WSAStartup()的返回值,如果为零,则成功启动网络库
char name[256]; // 指向接收本地主机名的缓冲区的指针。
PHOSTENT hostinfo; // PHOSTENT类型,本质上是一个指向hostent的结构体指针
wVersionRequest = MAKEWORD(2, 2);
iResult = WSAStartup(wVersionRequest, &wsaData);
if (0 != iResult) {
switch (iResult)
{
case WSASYSNOTREADY:
cout << "底层网络子系统还没有准备好进行网络通信。" << endl;
break;
case WSAVERNOTSUPPORTED:
cout << "此特定的Windows Sockets实现不提供所请求的Windows Sockets支持的版本。" << endl;
break;
case WSAEINPROGRESS:
cout << "正在执行阻塞Windows Sockets 1.1操作。" << endl;
break;
case WSAEPROCLIM:
cout << "已达到Windows Sockets实现支持的任务数量的限制。" << endl;
break;
case WSAEFAULT:
cout << "lpWSAData参数不是有效指针。" << endl;
break;
}
}
/*
确认WinSock DLL支持2.2。注意,如果DLL支持的版本除2.2外大于2.2,它仍将在wversion中返回2.2,因为这是我们请求的版本
*/
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion != 2)) {
cout << "找不到Winsock.dll的可用版本" << endl;
WSACleanup();
return 1;
}
else
cout << "发现了Winsock 2.2 DLL" << endl;
/*
gethostname中有两个参数,第一个name是指向接收主机名的缓冲区的指针,第二个参数是缓冲区的长度(以字节为单位)
gethostbyname中的参数是域名字符串或者指向主机名的指针
LPCSTR是Win32和VC++所使用的一种字符串数据类型。LPCSTR被定义成是一个指向以'\0'结尾的常量字符的指针。
inet_ntoa函数将(IPv4)Internet网络地址转换为Internet标准虚数点分十进制格式的ASCII字符串。
in_addr结构表示IPv4 Internet地址。
hostinfo在开头定义了,它是PHOSTENT类型,本质上是一个指向hostent的结构体指针,所以虽然没把hostent在代码中写出来,但必须弄清楚
h_addr_list,这个是地址列表,是hostent结构体中最重要的成员变量,通过该成员以整数形式保存域名对应的 IP 地址
*/
string ServAddr;
if (gethostname(name, sizeof(name)) == 0) {
if ((hostinfo = gethostbyname(name)) != NULL) {
//LPCSTR ip = inet_ntoa(*(struct in_addr*) * hostinfo->h_addr_list);
ServAddr = inet_ntoa(*(struct in_addr*) * hostinfo->h_addr_list);
//ServAddr = ip;
cout << "本机(此处作为服务器)的IP 为:" << ServAddr << endl;
//cout << "本机(此处作为服务器)的IP 为:" << ip << endl;
//如果将被注释掉的这三行来代替这两行代码的话也是可以的
}
}
else
{
switch (gethostname(name, sizeof(name)))
{
case WSAEFAULT:
cout << "name参数是空指针,或者不是用户地址空间的有效部分。如果namelen参数指定的缓冲区大小太小,无法容纳完整的主机名,也会返回此错误。" << endl;
break;
case WSANOTINITIALISED:
cout << "在使用此函数之前,必须进行成功的WSAstartup调用。" << endl;
break;
case WSAENETDOWN:
cout << "网络子系统失败。" << endl;
break;
case WSAEINPROGRESS:
cout << "阻塞Windows套接字1.1调用正在进行,或者服务提供者仍在处理回调函数。" << endl;
break;
}
}
WSACleanup(); // 在socket编程中,必须以WSACleanup()结尾
system("pause");
return 0;
}
和上一篇(网络编程实验1_(8)获得给定域名的IP地址(C++代码))一样,我通常喜欢把详细的解释放在注释中。
但是获得本机的IP地址和获得给定域名的IP地址有何区别呢?
相同之处在于我们使用的获得IP地址的方法都是通过gethostbyname()这个函数实现的。
这个函数的返回值为一个结构体指针,指向的结构体是hostent,可以通过转到定义查看,
我在程序中使用的就是这个结构体中的成员变量h_addr_list,这其实是一个指针数组,
用来存放解析出来的多个IP地址。
和获得给定域名IP地址不同的是,获得本机的IP地址用到了gethostname()这个函数。
这个函数有两个参数,第一个参数是指向接收主机名的缓冲区的指针,别被修饰词绕晕了,它是指向缓冲区的指针。
第二个参数是这个缓冲区的大小。
至于这个缓冲区的大小该设置为多少,MSDN上给出的解释是这样的(我翻译过后的):
所以我也就索性设置为了256字节。
这个函数如果运行成功了,返回一个零,否则返回一个错误码。
至于错误码的含义,你可以不用搞清楚,至少对于有耐心看我这个菜鸟写的博客的你来说,应该是没有任何意义的。
所以如果你想简单一点,可以把这个啰嗦的switch case去掉,从else开始,直接删掉也是没有任何问题的。
这个任务最重要的是理解hostent这个结构体,以及如何将这个结构体的成员变量中得到IP地址,并转换为我们需要的形式。
如果你觉得我讲的不够清楚的,可以看看我之前的博客,或者去网上查一查hostent、以及inet_ntoa()等等。