Visual Studio 控制台编程getXbyY()
实验目的
掌握Visual Studio控制台应用编程的基本方法
掌握Windows Sockets DLL的初始化和释放方法
掌握Windows Sockets API调用的一般步骤
使用Windows Sockets的API函数获得指定机器的信息
(包括主机名、服务名、协议、IP地址)
实验过程
实现 gethostbyname()函数
此函数含义为通过IP地址或网址输出相应主机的信息
实验环境:Visual Studio 2019
创建win32控制台应用程序项目
如图:
新建项目 -> Windows桌面向导 -> 创建 -> 控制台应用程序
创建出win32控制台应用程序
注:在vs2019中创建好此项目后不用再在代码中编写#include “stdafx.h”头文件,vs2019在项目中自带了此头文件,因此不用再次编写。
对项目属性进行一些修改:
修改字符集为多字节字符集(未修改可能导致IP地址等参数无法相匹配)
修改嵌入清单为否(在VS2010中不设置将编译错误)
命令参数中填入想获得信息的网址(以百度为例)
源代码
// gethostbyname.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib") //连接静态库
int main(int argc, char** argv)
{
//-----------------------------------------
// Declare and initialize variables
WSADATA wsaData;
int iResult;
DWORD dwError;
int i = 0;
struct hostent* remoteHost;//定义host entry结构体指针变量,定义见课件。
char* host_name;
struct in_addr addr; //以各种形式存放4个字节的IPv4地址
char** pAlias; //定义别名列表指针变量
// Validate the parameters
if (argc != 2) { //为何argc要等于2?,程序运行时每个参数分别是什么?
printf("usage: GetHostIP hostname\n"); //提示程序运行时需要输入主机名作为参数
return 1;
}
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);//与操作系统确认支持的WinSock版本
if (iResult != 0) {
printf("WSAStartup failed: %d\n", iResult);
return 1;
}
host_name = argv[1]; //提取用户输入的主机名
printf("Calling gethostbyname with %s\n", host_name);
remoteHost = gethostbyname(host_name);
if (remoteHost == NULL) { //报错,返回。
dwError = WSAGetLastError();
if (dwError != 0) {
if (dwError == WSAHOST_NOT_FOUND) {
printf("Host not found\n");
return 1;
}
else if (dwError == WSANO_DATA) {
printf("No data record found\n");
return 1;
}
else {
printf("Function failed with error: %ld\n", dwError);
return 1;
}
}
}
else { //依次显示获取的主机名称、多个别名、地址类型、地址长度、地址列表
printf("Function returned:\n");
printf("\tOfficial name: %s\n", remoteHost->h_name);//显示主机名称
for (pAlias = remoteHost->h_aliases; *pAlias != 0; pAlias++) { //循环显示别名
printf("\tAlternate name #%d: %s\n", ++i, *pAlias);
}
printf("\tAddress type: ");
switch (remoteHost->h_addrtype) {
case AF_INET:
printf("AF_INET\n"); //显示地址类型
break;
case AF_NETBIOS:
printf("AF_NETBIOS\n");
break;
default:
printf(" %d\n", remoteHost->h_addrtype);
break;
}
printf("\tAddress length: %d\n", remoteHost->h_length);//显示地址长度
i = 0;
if (remoteHost->h_addrtype == AF_INET)
{
while (remoteHost->h_addr_list[i] != 0) { //循环显示主机地址
addr.s_addr = *(u_long*)remoteHost->h_addr_list[i++];
printf("\tIP Address #%d: %s\n", i, inet_ntoa(addr));//将地址转换成点分十进制显示。
}
}
else if (remoteHost->h_addrtype == AF_NETBIOS)
{
printf("NETBIOS address was returned\n");
}
}
iResult = WSACleanup();
if (iResult != 0) {
printf("WSACleanup failed: %d\n", iResult);
return 1;
}
return 0;
}
运行结果:
代码分析
首先对WSAStartup()进行初始化
WSAStartup()函数声明如下
int WSAStartup ( WORD wVersionRequested, LPWSADATA lpWSAData );
其中WORD wVersionRequested 为socket版本,LPWSADATA lpWSAData 为相应WSAData变量,返回一个整数反应是否成功初始化。
Gethostbyname()函数原型为
struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name);
通过一个网站名返回此主机信息,主机信息存放在hostent结构体中,该函数调用DNS系统来获取对应域名或主机名的相关信息。
hostent结构体定义如下:
struct hostent{
char * h_name;
char ** h_aliases;
short h_addrtype;
short h_length;
char ** h_addr_list;
#define h_addr h_addr_list[0];
};
其中包含数据主机名、别名列表、地址类型、地址长度、地址列表
如果通过gethostbyname()函数得到的数据异常,则获取网站名失败,读取WSAGetLastError();函数得到错误原因代码赋值给dwError,有三种错误情况。通过dwError所得到的错误代码输出错误原因:主机未找到/无数据记录/函数错误以及代码
如果通过gethostbyname()函数得到的数据正常,则正常得到数据。由不同主机返回不同信息如地址类型等。