`

C++实现ping功能

 
阅读更多
基础知识

ping的过程是向目的IP发送一个type=8的ICMP响应请求报文,目标主机收到这个报文之后,会向源IP(发送方,我)回复一个type=0的ICMP响应应答报文。

那上面的字节、往访时间、TTL之类的信息又是从哪来的呢?这取决于IP和ICMP的头部。

#pragma once

//在默认windows.h会包含winsock.h,当你包含winsock2.h就会冲突,因此在包含windows.h前需要定义一个宏,#define WIN32_LEAN_AND_MEAN ;去除winsock.h
//要么将#include winsock2.h放在#includewindows.h前面或者直接去掉#includewindows.h

#include winsock2.h
#pragma comment(lib, WS2_32)     链接到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 CPing
{
public
    CPing();
    ~CPing();
    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;
};


#include "ping.h"
#include <iostream>
USHORT CPing::s_usPacketSeq = 0;

CPing::CPing() :m_szICMPData(NULL),m_bIsInitSucc(FALSE)
{
    WSADATA WSAData;
    //WSAStartup(MAKEWORD(2, 2), &WSAData);
    if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
    {
        /*如果初始化不成功则报错,GetLastError()返回发生的错误信息*/
        printf("WSAStartup() failed: %d\n", GetLastError());
        return;
    }
    m_event = WSACreateEvent();
    m_usCurrentProcID = (USHORT)GetCurrentProcessId();
    //setsockopt(m_sockRaw);
    /*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;
        }
    }*/
    m_sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
    if (m_sockRaw == INVALID_SOCKET)
    {
        std::cerr << "WSASocket() failed:" << WSAGetLastError ()<< std::endl;  //10013 以一种访问权限不允许的方式做了一个访问套接字的尝试。
    }
    else
    {
        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;
        }
    }
}

CPing::~CPing()
{
    WSACleanup();

    if (NULL != m_szICMPData)
    {
        free(m_szICMPData);
        m_szICMPData = NULL;
    }
}

BOOL CPing::Ping(DWORD dwDestIP, PingReply *pPingReply, DWORD dwTimeout)
{
    return PingCore(dwDestIP, pPingReply, dwTimeout);
}

BOOL CPing::Ping(char *szDestIP, PingReply *pPingReply, DWORD dwTimeout)
{
    if (NULL != szDestIP)
    {
        return PingCore(inet_addr(szDestIP), pPingReply, dwTimeout);
    }
    return FALSE;
}

BOOL CPing::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 CPing::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 CPing::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);
}


#include <winsock2.h>
#include <stdio.h>
#include "ping.h"

int main(void)
{
    CPing objPing;

    char *szDestIP = "127.0.0.1";
    PingReply reply;

    printf("Pinging %s with %d bytes of data:\n", szDestIP, DEF_PACKET_SIZE);
    while (TRUE)
    {
        objPing.Ping(szDestIP, &reply);
        printf("Reply from %s: bytes=%d time=%ldms TTL=%ld\n", szDestIP, reply.m_dwBytes, reply.m_dwRoundTripTime, reply.m_dwTTL);
        Sleep(500);
    }

    return 0;
}
分享到:
评论

相关推荐

    C++实现ping功能代码

    该编码用C++实现了ping功能,即PC机与PC机间的通讯功能。

    【非原创】C++实现PING功能源代码

    【非原创】C++实现PING功能源代码,经测试无BUG,已封装成函数,直接调用ping函数即可使用,信息反馈是通过shell输出,可自行更改。

    C++实现ping命令的功能(Windows)

    以上就是使用C++和WinSock2在Windows环境下实现ping命令的基本步骤。实际应用中,你可能还需要处理更多细节,如错误处理、多线程、命令行参数解析等。此外,由于Windows的安全策略限制,普通用户权限可能无法直接...

    C++ 实现 ping 功能解析实际 IP地址.rar_ip地址_ping_shinningr77_vc++_域名

    这个名为"C++ 实现 ping 功能解析实际 IP地址"的项目,旨在通过C++编程语言来模拟实现ping功能,不仅可以检测客户端与服务器之间的连通性,还能处理对IP地址和域名的解析。下面我们将深入探讨相关的知识点。 首先,...

    windows下 c++实现ping地址源码(两种)

    下面我们将深入探讨两种在Windows下用C++实现ping功能的方法,并结合Qt库进行讲解。 首先,我们要理解ping的基本原理。Ping是Internet控制消息协议(ICMP)的一部分,它发送一个称为echo请求的数据包到目标主机,...

    C++ 实现 ping 功能&& 域名(URL)解析实际 IP地址

    用C++实现了cmd命令行程序的ping命令,并能够将域名解析成实际的ip地址,详情请见博客地址:http://blog.csdn.net/goforwardtostep/article/details/52988142

    c++实现的ping

    C++实现ping功能时,需要创建一个ICMP类型的套接字,并发送ICMP回显请求到目标IP地址。 3. **数据包封装与解封装**:在发送ICMP回显请求时,需要将数据包封装成符合ICMP协议格式的结构体,包括类型、代码、校验和...

    ping功能C/C++实现

    用C语言实现ping功能的源码,已在VC6.0环境下编译通过

    wince ping 源码 c++版

    在Windows CE(简称WinCE)操作系统中,网络...通过阅读和分析这个源代码,你可以更好地理解和学习如何在WinCE环境下用C++实现ping功能。这个程序可以作为你自定义网络诊断工具的基础,或者对网络编程有更深入的理解。

    C++ 实现ping代码

    以下将详细介绍C++实现ping代码的相关知识点。 1. **C++编程语言**:C++是一种静态类型的、编译式的、通用的、大小写敏感的、不仅支持过程化编程,也支持面向对象编程的程序设计语言。在本项目中,C++被用来创建一...

    ping的c++的实现

    在IT行业中,网络编程是不可或缺的一部分,...总结起来,用C++实现ping功能涉及网络编程基础、ICMP协议的理解以及原始套接字的使用。这个过程不仅可以帮助开发者提升网络编程技能,也有助于在实际工作中解决网络问题。

    C++ ping 实现网络查看,原代码

    首先,让我们理解C++实现ping功能的基本原理。在C++中,我们不能直接调用操作系统底层的ping功能,而是需要通过系统调用来完成。通常,这涉及到使用套接字(socket)编程和发送ICMP数据包。在Windows操作系统上,...

    实现PING 的源代码

    通过分析和学习这个源代码,开发者不仅可以理解如何用C++实现ping功能,还能学习到MFC框架下的应用开发,以及如何利用Winsock进行网络编程。这对于深入理解和扩展网络相关的C++应用非常有帮助。

    Ping的C++实现

    标题 "Ping的C++实现" 涉及到的是在C++编程语言中构建一个功能类似于操作系统内置的`ping`命令的程序。`ping`是一个网络诊断工具,它通过发送Internet控制消息协议(ICMP)回显请求报文来检查网络连接是否可达。下面...

    linux下ping程序的实现

    可实现ping命令的ICMP包发送,对于了解及学习网络编程很有帮助

    学习ping原理必不可少的c++代码

    请大家编译成功后,在生成的exe文件右键管理员运行。

    基于socket实现Ping功能的源代码

    本文将深入探讨基于Socket实现Ping功能的源代码,涉及到的主要知识点包括Socket编程、原始套接字(SOCK_RAW)以及ICMP(Internet Control Message Protocol)协议。 首先,我们需要理解什么是Socket。Socket是操作...

    类似windows ping功能的源码

    2. **C++实现ping功能的关键点**: - **ICMP协议**:首先需要理解ICMP协议,它是Internet控制消息协议,主要用于错误报告和一些特殊控制信息。 - **套接字编程**:在C++中,我们需要使用套接字编程来创建和发送...

Global site tag (gtag.js) - Google Analytics