【TCP/IP】C语言实现Ping小程序

Ping程序一般用来测试一台主机是否可达,该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP回显 应答。

一般来说,如果不能Ping到某主机,那么就不能Telnet或者FTP到那台主机。反过来,如果不能Telnet到某台主机,那么通常可以用Ping程序来确定问题出在哪里。Ping程序还可以检测出到这台主机的往返时间,以表明该主机里我们有“多远”。大多数的TCP/IP实现都在内核中直接支持Ping服务器。

 

ICMP回显请求和回显应答报文如下所示

 /****************************************************************/
/*        类型(0或8)|        代码(0)|            校验和        |*/
/****************************************************************/
/*                标识符            |                序号        |*/
/****************************************************************/
/*                            选项数据                           |*/
/****************************************************************/


定义ICMP报头数据结构

typedef struct _ICMP_HEADER{
    BYTE nType;
    BYTE nCode;
    USHORT nCheckSum;
    USHORT nId;
    USHORT nSequence;
    UINT nTimeStamp;
}ICMP_HEADER,*PICMP_HEADER;


下面使用Socket实现Ping小程序。

// PingSock.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
#include <Windows.h>


//定义默认缓冲区长度
#define DEF_BUF_SIZE 1024
#define IP_HEADER_SIZE 20
#define ICMP_HEADER_SIZE (sizeof(ICMP_HEADER))
#define ICMP_DATA_SIZE 32
#define ICMP_PACK_SIZE (ICMP_HEADER_SIZE + ICMP_DATA_SIZE)


typedef struct _ICMP_HEADER{
    BYTE nType;
    BYTE nCode;
    USHORT nCheckSum;
    USHORT nId;
    USHORT nSequence;
    UINT nTimeStamp;
}ICMP_HEADER,*PICMP_HEADER;
 
char szInfo[DEF_BUF_SIZE] = {0};


USHORT GetCheckSum(LPBYTE lpBuf, DWORD dwSize);
BOOL Ping(char* lpDestIp);


int _tmain(int argc, _TCHAR* argv[])
{
    char szDestIp[DEF_BUF_SIZE] = {0} ;
    while ( scanf ( "%s", szDestIp) )
        Ping ( szDestIp ) ;
     
    return 0;
}


USHORT GetCheckSum(LPBYTE lpBuf, DWORD dwSize)
{
    DWORD dwCheckSum = 0;
    USHORT* lpWord = (USHORT*)lpBuf;


    while( dwSize > 1)
    {
        dwCheckSum += *lpWord++;
        dwSize -= 2;
    }


    if(1 == dwSize)
        dwCheckSum += *((USHORT*)lpBuf);


    dwCheckSum = ( dwCheckSum >> 16) + ( dwCheckSum & 0xffff);
    return (USHORT)(~dwCheckSum);
}


BOOL Ping(char* lpDestIp)
{
    SOCKADDR_IN DestAddr;
    DestAddr.sin_family = AF_INET;
    DestAddr.sin_addr.S_un.S_addr = inet_addr(lpDestIp);
    DestAddr.sin_port = htons(0);


    //创建ICMP请求包
    char ICMPPack[ICMP_PACK_SIZE] = {0};
    PICMP_HEADER pICMPHeader = (PICMP_HEADER)ICMPPack;
    pICMPHeader->nType = 8;
    pICMPHeader->nCode = 0;
    pICMPHeader->nId = (USHORT)::GetCurrentProcessId();
    pICMPHeader->nCheckSum = 0;
    pICMPHeader->nTimeStamp = 0;
    memset(&(ICMPPack[ICMP_HEADER_SIZE]),'E',ICMP_DATA_SIZE);


    //初始化WinSock
    WORD wVersionRequested = MAKEWORD(2,2);
    WSADATA wsaData;
    if(WSAStartup(wVersionRequested,&wsaData) != 0)
    {
        return FALSE;
    }


    //创建初始套接字
    SOCKET RawSock = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
    if(INVALID_SOCKET == RawSock)
    {
        printf("create raw socket error\n");
        return FALSE;
    }


    int nTime = 1000;
    int nRet = ::setsockopt( RawSock, SOL_SOCKET, SO_RCVTIMEO,(char*)(&nTime),sizeof(nTime));


    char szRecvBuf [ DEF_BUF_SIZE] ;
    SOCKADDR_IN    SourSockAddr ;


    for(int i = 0; i < 4; i++)
    {
        pICMPHeader->nCheckSum = 0;
        pICMPHeader->nTimeStamp = ::GetTickCount();
        pICMPHeader->nSequence = i;


        pICMPHeader->nCheckSum = GetCheckSum ( (LPBYTE)ICMPPack, ICMP_PACK_SIZE ) ;


        int nRet = ::sendto( RawSock, ICMPPack, ICMP_PACK_SIZE, 0, (SOCKADDR*)&DestAddr, sizeof(DestAddr));
        if ( nRet == SOCKET_ERROR )
        {
            printf ( "sendto error!\n" ) ;
            return FALSE ;
        }


        // 接收ICMP响应
        int nLen = sizeof(SourSockAddr) ;
        nRet = ::recvfrom ( RawSock, szRecvBuf, DEF_BUF_SIZE,0,(SOCKADDR*)&SourSockAddr, &nLen ) ;
        if ( nRet == SOCKET_ERROR )
        {
            if ( ::WSAGetLastError() == WSAETIMEDOUT )
            {
                printf ( "Request Timeout\n" ) ;
                continue ;
            }
            else
            {
                printf ( "recvfrom error!\n" ) ;
                return FALSE ;
            }
        }


        int nTime = ::GetTickCount() - pICMPHeader->nTimeStamp ;


        int nRealSize = nRet - IP_HEADER_SIZE - ICMP_HEADER_SIZE ;
        if ( nRealSize < 0  )
        {
            printf ( "To less recv bytes!\n" ) ;
            continue ;
        }


        // 检测是否当前所发出的ICMP响应包
        PICMP_HEADER pRecvHeader = (PICMP_HEADER)(szRecvBuf+IP_HEADER_SIZE) ;
        if ( pRecvHeader->nType != 0 )
        {
            printf ( "Not ICMP respond type!\n" ) ;
            return FALSE ;
        }


        if ( pRecvHeader->nId != ::GetCurrentProcessId () )
        {
            printf ( "not valid id!\n" ) ;
            return FALSE ;
        }


        printf ( "%d bytes replay from %s : bytes=%d time=%dms\n", \
            nRet, inet_ntoa(SourSockAddr.sin_addr), nRealSize, nTime ) ;


        ::Sleep ( 1000 ) ;
    }


    closesocket ( RawSock ) ;
    WSACleanup () ;


    return TRUE ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值