2.1 获取本地计算机的名称和IP
网络中的主机通常有一个主机名称和一个或多个IP地址。有了这些标识,其他主机就能找到我们,就能和我们通信。
2.1.1 gethostname函数
gethostname函数用来检索本地计算机的标准主机名,函数声明如下:
int gethostname(char *name, int namelen);
其中,参数name指向接收本地主机名的缓冲区的指针;namelen表示name所指缓冲区的长度,以字节为单位。如果没有出现错误,那么函数返回零;否则,它将返回SOCKET_ERROR,可以通过调用WSAGetLastError来检索特定的错误代码。
例如,我们可以利用下面的代码来获取本地计算机的名称:
char szHostName[128]; char szT[20]; if( gethostname(szHostName, 128) == 0 ) puts("本地计算机名称是:%s", szHostName);
2.1.2 gethostbyname函数
gethostbyname函数从主机数据库中检索与主机名对应的主机信息,比如IP地址等,函数声明如下:
hostent * gethostbyname( const char *name);
其中,参数name是本地计算机的名称,可以用gethostname获得。如果没有出现错误,就将返回指向hostent结构的指针;否则,将返回一个空指针,并且可以通过调用WSAGetLastError来检索特定的错误号,比如错误号是WSANOTINITIALISED,表示没有预先成功调用WSAStartup函数。
hostent是一个结构体,定义如下:
typedef struct hostent { char *h_name; char **h_aliases; short h_addrtype; short h_length; char **h_addr_list; } HOSTENT, *PHOSTENT, *LPHOSTENT;
其中,h_name表示主机的正式名称。h_aliases指向以NULL结尾的主机别名数组;h_addrtype返回地址类型;h_length表示ip地址的长度,ipv4对应4个字节;一般主机可以有多个ip地址,比如www.163.com就有121.14.228.43和121.11.151.72两个ip,h_addr_list就用来保存多个ip地址。
在Internet环境下为AF-INET;h_length表示地址的字节长度;h_addr_list指向一个以NULL结尾的数组,包含该主机的所有地址。
例如,下面的代码可以获得本机所有的IP地址:
struct hostent * pHost; int i; pHost = gethostbyname(szHostName); for( i = 0; pHost!= NULL && pHost->h_addr_list[i]!= NULL; i++ ) { char str[100]; char addr[20]; int j; LPCSTR psz=inet_ntoa (*(struct in_addr *)pHost->h_addr_list[i]); m_IPAddr.AddString(psz); }
2.1.3 inet_ntoa函数
inet_ntoa该函数将一个十进制网络字节序转换为点分十进制IP格式的字符串,函数声明如下:
char*inet_ntoa(struct in_addr in);
其中,参数in表示Internet主机地址的结构,结构体in_addr在第7章中会介绍。如果函数正确,就返回一个字符指针,指向一块存储着点分格式IP地址的静态缓冲区;如果错误,就返回NULL。
下面我们看一个例子,是一个对话框程序,用来获取本机的名称和IP地址。如果大家对于对话框编程不熟悉,可以参考笔者关于VC2017开发的书籍《Visual C++ 2017从入门到精通》。
【例2.1】获取本机名称和IP地址
(1)新建一个对话框工程,工程名是test。
(2)切换到资源视图,打开对话框编辑器,在对话框上添加一个编辑框、一个列表框(List Box)和一个按钮。其中,编辑框用来显示本机名称,列表框用来显示本机的IP地址。设置按钮的标题是“查询”。为编辑添加控件类型变量m_HostName,为列表框添加控件变量m_IPAddr。
(3)设置工程属性。打开工程属性对话框,设置工程为多字节字符工程,如图2-1所示。
图2-1
展开“C/C++”→“Preprocessor”,在右边第一行“Preprocessor Definitions”旁的开头添加一个宏:
_WINSOCK_DEPRECATED_NO_WARNINGS;
注意有个分号。有了这个宏,就可以使用一些传统函数了,而不会出现警告,如图2-2所示。
图2-2
(4)切换类视图,选择CtestApp,对其成员函数InitInstance双击,在InitInstance函数中添加Winsock库初始化代码:
if (!AfxSocketInit()) { AfxMessageBox("AfxSocketInit failed"); return FALSE; }
在test.h开头添加头文件包含:
#include <afxsock.h>
(5)切换到资源视图,在对话框界面中的“查询”按钮上双击,添加事件响应代码:
void CtestDlg::OnBnClickedButton1() { // TODO: Add your control notification handler code here char szHostName[128]; char szT[20]; if (gethostname(szHostName, 128) == 0)//获取本机名称 { // Get host adresses m_HostName.SetWindowText(szHostName);//本机名称显示在编辑框中 struct hostent * pHost; int i; pHost = gethostbyname(szHostName);//获取本机网络信息 for (i = 0; pHost != NULL && pHost->h_addr_list[i] != NULL; i++) { char str[100]; char addr[20]; int j; LPCSTR psz = inet_ntoa(*(struct in_addr *)pHost->h_addr_list[i]); m_IPAddr.AddString(psz); //把IP字符串显示在列表框中 } } }
在代码中,首先获取了本机名称,然后显示在编辑框中,接着获取本机网络信息,最后显示在列表框中。
(6)保存工程并运行,结果如图2-3所示。
图2-3