Windows C++ ping命令实现

摘录知名博主的源码,亲测可以,如下:

#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")

// IP数据包头结构
typedef struct iphdr
{
    unsigned int headLen:4;
    unsigned int version:4;
    unsigned char tos;
    unsigned short totalLen;
    unsigned short ident;
    unsigned short fragAndFlags;
    unsigned char ttl;
    unsigned char proto;
    unsigned short checkSum;
    unsigned int sourceIP;
    unsigned int destIP;
}IpHeader;

// ICMP数据头结构
typedef struct ihdr
{
    unsigned char iType;
    unsigned char iCode;
    unsigned short iCheckSum;
    unsigned short iID;
    unsigned short iSeq;
    unsigned long  timeStamp;
}IcmpHeader;

// 计算ICMP包的校验和(发送前要用)
unsigned short checkSum(unsigned short *buffer, int size)
{
    unsigned long ckSum = 0;

    while(size > 1)
    {
        ckSum += *buffer++;
        size -= sizeof(unsigned short);
    }

    if(size)
    {
        ckSum += *(unsigned char*)buffer;
    }

    ckSum = (ckSum >> 16) + (ckSum & 0xffff);
    ckSum += (ckSum >>16);

    return unsigned short(~ckSum);
}

// 填充ICMP请求包的具体参数
void fillIcmpData(char *icmpData, int dataSize)
{
    IcmpHeader *icmpHead = (IcmpHeader*)icmpData;
    icmpHead->iType = 8;  // 8表示请求包
    icmpHead->iCode = 0;
    icmpHead->iID = (unsigned short)GetCurrentThreadId();
    icmpHead->iSeq = 0;
    icmpHead->timeStamp = GetTickCount();
    char *datapart = icmpData + sizeof(IcmpHeader);
    memset(datapart, 'x', dataSize - sizeof(IcmpHeader)); // 数据部分为xxx..., 实际上有32个x
    icmpHead->iCheckSum = checkSum((unsigned short*)icmpData, dataSize); // 千万要注意,这个一定要放到最后
}

// 对返回的IP数据包进行解析,定位到ICMP数据
int decodeResponse(char *buf, int bytes, struct sockaddr_in *from, int tid)
{
    IpHeader *ipHead = (IpHeader *)buf;
    unsigned short ipHeadLen = ipHead->headLen * 4 ;
    if (bytes < ipHeadLen + 8) // ICMP数据不完整, 或者不包含ICMP数据
    {
        return -1;
    }

    IcmpHeader *icmpHead = (IcmpHeader*)(buf + ipHeadLen);  // 定位到ICMP包头的起始位置
    if (icmpHead->iType != 0)   // 0表示回应包
    {
        return -2;
    }

    if (icmpHead->iID != (unsigned short)tid) // 理应相等
    {
        return -3;
    }

    int time = GetTickCount() - (icmpHead->timeStamp); // 返回时间与发送时间的差值
    if(time >= 0)
    {
        return time;
    }

    return -4; // 时间错误
}

// ping操作
int ping(const char *ip, unsigned int timeout)
{
    // 网络初始化
    WSADATA wsaData;
    WSAStartup(MAKEWORD(1, 1), &wsaData);
    unsigned int sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);  // 注意,第三个参数非常重要,指定了是icmp
    setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));  // 设置套接字的接收超时选项
    setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));  // 设置套接字的发送超时选项

    // 准备要发送的数据
    int  dataSize = sizeof(IcmpHeader) + 32; // 待会儿会有32个x
    char icmpData[1024] = {0};
    fillIcmpData(icmpData, dataSize);
    unsigned long startTime = ((IcmpHeader *)icmpData)->timeStamp;

    // 远程通信端
    struct sockaddr_in dest;
    memset(&dest, 0, sizeof(dest));
    struct hostent *hp = gethostbyname(ip);
    memcpy(&(dest.sin_addr), hp->h_addr, hp->h_length);
    dest.sin_family = hp->h_addrtype;

    // 发送数据
    sendto(sockRaw, icmpData, dataSize, 0, (struct sockaddr*)&dest, sizeof(dest));

    int iRet = -1;
    struct sockaddr_in from;
    int fromLen = sizeof(from);
    while(1)
    {
        // 接收数据
        char recvBuf[1024] = {0};
        int iRecv = recvfrom(sockRaw, recvBuf, 1024, 0, (struct sockaddr*)&from, &fromLen);
        int time  = decodeResponse(recvBuf, iRecv, &from, GetCurrentThreadId());
        if(time >= 0)
        {
            iRet = 0;   // ping ok
            break;
        }
        else if( GetTickCount() - startTime >= timeout || GetTickCount() < startTime)
        {
            iRet = -1;  // ping超时
            break;
        }
    }

    // 释放
    closesocket(sockRaw);
    WSACleanup();

    return iRet;
}

// 主函数
int main()
{
    // 请用格式合理的ip地址
    char szIPs[][16] =
    {
        "192.168.1.1",
        "192.168.1.100",
        "192.168.1.101"
    };

    int i = 0;
    int size = sizeof(szIPs) / sizeof(szIPs[0]);
    for(i = 0; i < size; i++)
    {
        int iRet = ping(szIPs[i], 3000); // 超时时间为3000ms

        if(0 == iRet)
        {
            printf("%s ONLINE\n", szIPs[i]);
        }
        else if(-1 == iRet)
        {
            printf("%s TIMEOUT\n", szIPs[i]);
        }
        else
        {
            printf("Unkonw");
        }
    }

    return 0;
}

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Ping命令可以用C++语言通过发送ICMP包来实现。以下是一个简单的实现: ```c++ #include <iostream> #include <winsock2.h> #include <iphlpapi.h> #include <icmpapi.h> #pragma comment(lib, "iphlpapi.lib") #pragma comment(lib, "ws2_32.lib") int main(int argc, char* argv[]) { WSADATA wsaData; int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (iResult != 0) { std::cout << "WSAStartup failed: " << iResult << std::endl; return 1; } if (argc < 2) { std::cout << "Usage: ping <hostname or IP address>" << std::endl; return 1; } std::string host = argv[1]; HANDLE hIcmpFile = IcmpCreateFile(); if (hIcmpFile == INVALID_HANDLE_VALUE) { std::cout << "IcmpCreateFile failed: " << GetLastError() << std::endl; WSACleanup(); return 1; } char SendData[] = "Ping"; LPVOID ReplyBuffer = malloc(sizeof(ICMP_ECHO_REPLY) + sizeof(SendData)); if (ReplyBuffer == NULL) { std::cout << "malloc failed: " << GetLastError() << std::endl; IcmpCloseHandle(hIcmpFile); WSACleanup(); return 1; } DWORD ReplySize = sizeof(ICMP_ECHO_REPLY) + sizeof(SendData); DWORD dwRetVal = IcmpSendEcho(hIcmpFile, inet_addr(host.c_str()), SendData, sizeof(SendData), NULL, ReplyBuffer, ReplySize, 1000); if (dwRetVal != 0) { PICMP_ECHO_REPLY pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer; std::cout << "Ping " << host << " successful: " << "Reply from " << inet_ntoa(pEchoReply->Address) << " time=" << pEchoReply->RoundTripTime << "ms" << std::endl; } else { std::cout << "Ping " << host << " failed: " << GetLastError() << std::endl; } free(ReplyBuffer); IcmpCloseHandle(hIcmpFile); WSACleanup(); return 0; } ``` 运行程序时,通过命令行参数传入要ping的主机名或IP地址。程序将发送一个ICMP包到目标主机,并等待回复。如果收到回复,程序输出回复信息,否则输出错误信息。注意,该程序仅在Windows环境下可用。在Linux环境下,可以使用类似于该程序的套接字编程方法来实现Ping命令

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值