2016-11-03
个评论 来源:
前行中的小猪我要投稿1、简述
一般情况下,我们想知道在当前电脑设备环境下,某一个网址能不能访问,最简单的方法是win + R 键 ,输入cmd,召唤cmd命令行程序,然后直接用ping命令 + 网址 来看返回的结果,那么我们是通过windows提供的工具来得到相应的结果,那我们能不能自己用代码实现呢?
答案肯定是可以的,在我们输入ping命令后,cmd.exe解析后就进行相应的操作,而我们就是去实现这个操作,下面就讲述一下如何用代码实现。
2、代码之路
我们先来看看用windows提供的cmd命令行程序通过ping命令得到的结果。
这里我们 ping 了 百度的网址,下图为 ping 了 两次得到的结果,我们发现返回来的实际IP地址不一样,这里也很容易理解,因为我们在
浏览器访问一个网址,是经过DNS域名解析服务器根据主机名解析得到对应的IP地址,而百度在各个地方有多台服务器,所以这里返回的IP并不止一个。
我们可以直接ping + 域名(也就是网址) , 也可以 ping + IP(这里也就是域名对应的实际IP地址),我们看到,通过ping www.baidu.com ,我们可以看到实际的IP地址,这里我们也可以直接ping + IP得到返回结果。
我们也看到了通过ping可以得到域名对应的实际IP地址,那我们也可以通过代码来实现,并且能够返回域名对应的全部IP地址。
域名解析实际IP地址
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
BOOL GetRealIpByDomainName(char *szHost, char szIp[50][100], int *nCount)
{
WSADATA wsaData;
HOSTENT *pHostEnt;
int nAdapter = 0;
struct sockaddr_in sAddr;
if (WSAStartup(0x0101, &wsaData))
{
printf(" gethostbyname error for host:\n");
return FALSE;
}
pHostEnt = gethostbyname(szHost);
if (pHostEnt)
{
while (pHostEnt->h_addr_list[nAdapter])
{
memcpy(&sAddr.sin_addr.s_addr, pHostEnt->h_addr_list[nAdapter], pHostEnt->h_length);
sprintf_s(szIp[nAdapter], "%s", inet_ntoa(sAddr.sin_addr));
nAdapter++;
}
*nCount = nAdapter;
}
else
{
DWORD dwError = GetLastError();
*nCount = 0;
}
WSACleanup();
return TRUE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//测试代码
void main()
{
// 返回的域名对应实际IP的个数
int nIpCount = 0;
// 返回的域名对应实际I列表
char szIpList[50][100];
// 域名
char szDomain[256] = { 0 };
char szIp[1024] = { 0 };
strcpy_s(szDomain, "www.baidu.com");
GetIpByDomainName(szDomain, szIpList, &nIpCount);
for (int i = 0; i < nIpCount; i++)
{
strcat_s(szIp, szIpList[i]);
strcat_s(szIp, "\t");
}
printf("DomainName : %s \n", szDomain);
printf("Real IPList : %s", szIp);
}
测试结果
可以看出结果与用cmd命令行程序得到的结果一致。
这里 www.baidu.com 对应两个IP。这里是我这个地区得到的IP,其他地区以cmd命令行程序 ping的结果为准。ping的前提下是本地没有配置host文件,可以在C:\Windows\System32\drivers\etc\hosts 这个文件中查看是否将某些域名配置成了固定的IP地址。
hosts简介
hosts文件其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“
数据库”,当用户在浏览器中输入一个需要登录的网址时,
系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交DNS域名解析服务器进行IP地址的解析。
hosts小应用
hosts文件就相当于本地网址的第一层过滤,可以把一些不想访问的网址进行过滤(比如恶意弹出的广告和网页游戏等),达到一层保护的效果
比如在hosts文件中添加 www.baidu.com 127.0.0.1 , 这样我们就访问不了百度网址了。
前几天也在某一篇文章中看到通过修改hosts文件配置可以过滤播放器的一些视频广告,具体做法详情百度一下哈 (^o^)/~。
C++ 实现 ping 功能
以下代码实现了我们的 cmd命令行程序中的ping 功能,但是只能够ping + IP 地址,不能够进行ping + 域名 ,需要配合上面的代码进行使用。
parseurl.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#pragma once
#include <winsock2.h>
#include<stdlib.h>
#pragma comment(lib, "Ws2_32.lib")
#define DEF_PACKET_SIZE 32
#define ECHO_REQUEST 8
#define ECHO_REPLY 0
struct IPHeader
{
BYTE m_byVerHLen; //4位版本+4位首部长度
BYTE m_byTOS; //服务类型
USHORT m_usTotalLen; //总长度
USHORT m_usID; //标识
USHORT m_usFlagFragOffset; //3位标志+13位片偏移
BYTE m_byTTL; //TTL
BYTE m_byProtocol; //协议
USHORT m_usHChecksum; //首部检验和
ULONG m_ulSrcIP; //源IP地址
ULONG m_ulDestIP; //目的IP地址
};
struct ICMPHeader
{
BYTE m_byType; //类型
BYTE m_byCode; //代码
USHORT m_usChecksum; //检验和
USHORT m_usID; //标识符
USHORT m_usSeq; //序号
ULONG m_ulTimeStamp; //时间戳(非标准ICMP头部)
};
struct PingReply
{
USHORT m_usSeq;
DWORD m_dwRoundTripTime;
DWORD m_dwBytes;
DWORD m_dwTTL;
};
class ParseUrl
{
public:
ParseUrl();
~ParseUrl();
BOOL Ping(DWORD dwDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 2000);
BOOL Ping(char *szDestIP, PingReply *pPingReply = NULL, DWORD dwTimeout = 2000);
private:
BOOL PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout);
USHORT CalCheckSum(USHORT *pBuffer, int nSize);
ULONG GetTickCountCalibrate();
private:
SOCKET m_sockRaw;
WSAEVENT m_event;
USHORT m_usCurrentProcID;
char *m_szICMPData;
BOOL m_bIsInitSucc;
private:
static USHORT s_usPacketSeq;
};</stdlib.h></winsock2.h>
parseurl.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include "stdafx.h"
#include "parseurl.h"
USHORT ParseUrl::s_usPacketSeq = 0;
ParseUrl::ParseUrl() :
m_szICMPData(NULL),
m_bIsInitSucc(FALSE)
{
WSADATA WSAData;
WSAStartup(MAKEWORD(1, 1), &WSAData);
m_event = WSACreateEvent();
m_usCurrentProcID = (USHORT)GetCurrentProcessId();
if ((m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0)) != SOCKET_ERROR)
{
WSAEventSelect(m_sockRaw, m_event, FD_READ);
m_bIsInitSucc = TRUE;
m_szICMPData = (char*)malloc(DEF_PACKET_SIZE + sizeof(ICMPHeader));
if (m_szICMPData == NULL)
{
m_bIsInitSucc = FALSE;
}
}
}
ParseUrl::~ParseUrl()
{
WSACleanup();
if (NULL != m_szICMPData)
{
free(m_szICMPData);
m_szICMPData = NULL;
}
}
BOOL ParseUrl::Ping(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{
return PingCore(dwDestIP, pPingReply, dwTimeout);
}
BOOL ParseUrl::Ping(char *szDestIP, PingReply *pPingReply, DWORD dwTimeout)
{
if (NULL != szDestIP)
{
return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout);
}
return FALSE;
}
BOOL ParseUrl::PingCore(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{
//判断初始化是否成功
if (!m_bIsInitSucc)
{
return FALSE;
}
//配置SOCKET
sockaddr_in sockaddrDest;
sockaddrDest.sin_family = AF_INET;
sockaddrDest.sin_addr.s_addr = dwDestIP;
int nSockaddrDestSize = sizeof(sockaddrDest);
//构建ICMP包
int nICMPDataSize = DEF_PACKET_SIZE + sizeof(ICMPHeader);
ULONG ulSendTimestamp = GetTickCountCalibrate();
USHORT usSeq = ++s_usPacketSeq;
memset(m_szICMPData, 0, nICMPDataSize);
ICMPHeader *pICMPHeader = (ICMPHeader*)m_szICMPData;
pICMPHeader->m_byType = ECHO_REQUEST;
pICMPHeader->m_byCode = 0;
pICMPHeader->m_usID = m_usCurrentProcID;
pICMPHeader->m_usSeq = usSeq;
pICMPHeader->m_ulTimeStamp = ulSendTimestamp;
pICMPHeader->m_usChecksum = CalCheckSum((USHORT*)m_szICMPData, nICMPDataSize);
//发送ICMP报文
if (sendto(m_sockRaw, m_szICMPData, nICMPDataSize, 0, (struct sockaddr*)&sockaddrDest, nSockaddrDestSize) == SOCKET_ERROR)
{
return FALSE;
}
//判断是否需要接收相应报文
if (pPingReply == NULL)
{
return TRUE;
}
char recvbuf[256] = { "\0" };
while (TRUE)
{
//接收响应报文
if (WSAWaitForMultipleEvents(1, &m_event, FALSE, 100, FALSE) != WSA_WAIT_TIMEOUT)
{
WSANETWORKEVENTS netEvent;
WSAEnumNetworkEvents(m_sockRaw, m_event, &netEvent);
if (netEvent.lNetworkEvents & FD_READ)
{
ULONG nRecvTimestamp = GetTickCountCalibrate();
int nPacketSize = recvfrom(m_sockRaw, recvbuf, 256, 0, (struct sockaddr*)&sockaddrDest, &nSockaddrDestSize);
if (nPacketSize != SOCKET_ERROR)
{
IPHeader *pIPHeader = (IPHeader*)recvbuf;
USHORT usIPHeaderLen = (USHORT)((pIPHeader->m_byVerHLen & 0x0f) * 4);
ICMPHeader *pICMPHeader = (ICMPHeader*)(recvbuf + usIPHeaderLen);
if (pICMPHeader->m_usID == m_usCurrentProcID //是当前进程发出的报文
&& pICMPHeader->m_byType == ECHO_REPLY //是ICMP响应报文
&& pICMPHeader->m_usSeq == usSeq //是本次请求报文的响应报文
)
{
pPingReply->m_usSeq = usSeq;
pPingReply->m_dwRoundTripTime = nRecvTimestamp - pICMPHeader->m_ulTimeStamp;
pPingReply->m_dwBytes = nPacketSize - usIPHeaderLen - sizeof(ICMPHeader);
pPingReply->m_dwTTL = pIPHeader->m_byTTL;
return TRUE;
}
}
}
}
//超时
if (GetTickCountCalibrate() - ulSendTimestamp >= dwTimeout)
{
return FALSE;
}
}
}
USHORT ParseUrl::CalCheckSum(USHORT *pBuffer, int nSize)
{
unsigned long ulCheckSum = 0;
while (nSize > 1)
{
ulCheckSum += *pBuffer++;
nSize -= sizeof(USHORT);
}
if (nSize)
{
ulCheckSum += *(UCHAR*)pBuffer;
}
ulCheckSum = (ulCheckSum >> 16) + (ulCheckSum & 0xffff);
ulCheckSum += (ulCheckSum >> 16);
return (USHORT)(~ulCheckSum);
}
ULONG ParseUrl::GetTickCountCalibrate()
{
static ULONG s_ulFirstCallTick = 0;
static LONGLONG s_ullFirstCallTickMS = 0;
SYSTEMTIME systemtime;
FILETIME filetime;
GetLocalTime(&systemtime);
SystemTimeToFileTime(&systemtime, &filetime);
LARGE_INTEGER liCurrentTime;
liCurrentTime.HighPart = filetime.dwHighDateTime;
liCurrentTime.LowPart = filetime.dwLowDateTime;
LONGLONG llCurrentTimeMS = liCurrentTime.QuadPart / 10000;
if (s_ulFirstCallTick == 0)
{
s_ulFirstCallTick = GetTickCount();
}
if (s_ullFirstCallTickMS == 0)
{
s_ullFirstCallTickMS = llCurrentTimeMS;
}
return s_ulFirstCallTick + (ULONG)(llCurrentTimeMS - s_ullFirstCallTickMS);
}
测试ping + Ip 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main()
{
ParseUrl objParseUrl;
char *szDestIP = "112.80.248.73";
PingReply reply;
printf("Pinging %s with %d bytes of data:\n\n", szDestIP, DEF_PACKET_SIZE);
for (int i = 0; i < 4; i++)
{
objParseUrl.Ping(szDestIP, &reply);
printf("Reply from %s: bytes=%ld time=%ldms TTL=%ld\n", szDestIP, reply.m_dwBytes, reply.m_dwRoundTripTime, reply.m_dwTTL);
Sleep(500);
}
}
测试结果
测试 结合域名解析实际IP地址 GetIpByDomainName方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void pingIp(char Ip[100])
{
ParseUrl objParseUrl;
PingReply reply;
printf("\nPinging %s with %d bytes of data:\n\n", Ip, DEF_PACKET_SIZE);
for (int i = 0; i < 4; i++)
{
objParseUrl.Ping(Ip, &reply);
printf("Reply from %s: bytes=%ld time=%ldms TTL=%ld\n", Ip, reply.m_dwBytes, reply.m_dwRoundTripTime, reply.m_dwTTL);
Sleep(500);
}
}
void main()
{
int nIpCount = 0;
char szIpList[50][100];
char szDomain[256] = { 0 };
char szIp[1024] = { 0 };
strcpy_s(szDomain, "www.baidu.com");
GetIpByDomainName(szDomain, szIpList, &nIpCount);
printf("域名 : %s \n", szDomain);
for (int i = 0; i < nIpCount; i++)
{
pingIp(szIpList[i]);
strcat_s(szIp, szIpList[i]);
strcat_s(szIp, "\t");
}
printf("\n域名解析IP列表 : %s \n\n", szIp);
}
测试结果
尾
以上即为本篇文章的内容,通过两段代码结合实现了cmd命令行程序的ping功能,这里我们也可以用来判断当前设备有没有联网,在某些环境需要判断当前设备是否连接互联网是非常有必要的,在
Qt QTcpSocket 对连接服务器中断的不同情况进行判定 这篇文章中,我们叙述了提供几种不同的方法检测客户端与服务器断开的几种方法,其中也提到了使用ping方法来判断本机是否联网,其中使用了Qt封装的库,直接调用即可,方便快捷,但是如果不是Qt程序,可以利用此篇文章提供的C++方法。
同时在
Qt QTcpSocket 对连接服务器中断的不同情况进行判定 中也提到了一种更直接的方法,通过windows提供的IsNetworkAlive判断本地是否有网络连接,但是不能判断是否能访问互联网,这就需要通过ping 方法来判断了。可以直接在我贡献的资源中下载
源码,进行使用。
代码下载
C++ 实现 ping 功能&& 域名(URL)解析实际 IP地址