计算机网络---用C++实现ping程序源代码

本文详细介绍了如何使用C++和Winsock库实现一个Ping工具,包括数据包的构造、发送和接收过程,以及如何计算往返时间和TTL。通过具体代码示例,读者可以了解网络编程的基本原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

//----------------------头文件------------------------
#include <stdio.h> 
#include <stdlib.h> 
#include <winsock.h> 
#include<conio.h> 
#pragma comment(lib, "ws2_32.lib")   //提供对以下网络相关API的支持
#define REPLY 0                      //ICMP 回送回应报文 
#define REQUEST 8                    //ICMP 回送请求报文
#define DATASIZE 32                  // 请求数据报的大小
#include <iostream>
using namespace std; 

//----------------------数据结构定义------------------------
//定义IP首部的数据结构
typedef struct IPHeader 
{ 
    unsigned char VerLenth;      //定义首部长度和版本,高4位为首部长度,低4位为版本
    unsigned char Tos;           //服务类型/区分服务
    unsigned short TotalLenth;   //总长度 
    unsigned short ID;           //标识号 
    unsigned short Frag_Flags;   //片偏移量 
    unsigned char TTL;           //生存时间 
    unsigned char Protocol;      //协议 
    unsigned short Checksum;     //首部校验和
    struct in_addr SrcIP;        //源 IP 地址 
    struct in_addr DestIP;       //目的地址
}IPHDR, *PIPHDR; 

//定义 ICMP 首部格式
typedef struct ICMPHeader 
{ 
    unsigned char Type;         //类型 
    unsigned char Code;         //代码 
    unsigned short Checksum;    //首部校验和 
    unsigned short ID;          //标识 
    unsigned short Seq;         //序列号
    char Data;                  //数据
}ICMPHDR, *PICMPHDR; 
//定义 ICMP 回应请求
typedef struct ECHOREQUEST 
{ 
    ICMPHDR icmpHdr; 
    DWORD dwTime; 
    char cData[DATASIZE]; 
}ECHOREQUEST, *PECHOREQUEST; 

//定义 ICMP 回应答复
typedef struct ECHOREPLY 
{ 
    IPHDR ipHdr; 
    ECHOREQUEST echoRequest; 
    char cFiller[256]; 
}ECHOREPLY,*PECHOREPLY;

//-----------------------------------------------------
//计算校验和
unsigned short CheckSum(unsigned short *buffer, int len) 
{ 
    register int nleft = len; 
    register unsigned short *w = buffer; 
    register unsigned short answer; 
    register int sum = 0; 
    //使用32的位累加器,进行16位的计算
    while ( nleft > 1 ) 
    { 
        sum += *w++; 
        nleft -= 2; 
    } 
    if ( nleft == 1 ) 
    { 
        unsigned short u = 0; 
        *(unsigned char *)(&u) = *(unsigned char*)w; 
        sum += u; 
    }
    //将反馈的 16 位从高位移到低位
    sum = (sum >> 16) + (sum & 0xffff); 
    sum += (sum >> 16); 
    answer = ~sum; 
    return (answer); 
} 


//发送回应请求函数
int SendRequest(SOCKET s, struct sockaddr_in *lpstToAddr) 
{ 
    static ECHOREQUEST echoReq; 
    static int nId = 1; 
    static int nSeq = 1; 
    int nRet; 
    //填充回应请求消息
    echoReq.icmpHdr.Type = REQUEST; 
    echoReq.icmpHdr.Code = 0; 
    echoReq.icmpHdr.Checksum = 0; 
    echoReq.icmpHdr.ID = nId++; 
    echoReq.icmpHdr.Seq = nSeq++; 
    //填充要发送的数据
    for (nRet = 0; nRet < DATASIZE; nRet++) 
    { 
        echoReq.cData[nRet] = '1' + nRet; 
    } 
    //存储发送的时间
    echoReq.dwTime = GetTickCount(); 
    //计算回应请求的校验和
    echoReq.icmpHdr.Checksum = CheckSum((unsigned short*)&echoReq, sizeof(ECHOREQUEST)); 
    //发送回应请求
    nRet = sendto(s,(LPSTR)&echoReq,sizeof(ECHOREQUEST),0,(struct sockaddr*)lpstToAddr,sizeof(SOCKADDR_IN)); 
    if (nRet == SOCKET_ERROR) 
    { 
        printf("发送失败\t error:%d\n", WSAGetLastError()); 
    } 
    return (nRet); 
} 

//接收应答回复并进行解析
DWORD RecvReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, unsigned char *pTTL) 
{ 
    ECHOREPLY echoReply; 
    int nRet; 
    int nAddrLen = sizeof(struct sockaddr_in); 
    //接收应答回复
    nRet = recvfrom(s,(LPSTR)&echoReply,sizeof(ECHOREPLY),0,(LPSOCKADDR)lpsaFrom,&nAddrLen); 
    //检验接收结果
    if (nRet == SOCKET_ERROR) 
    { 
        printf("接收失败\t error:%d\n",WSAGetLastError()); 
    } 
    //记录返回的 TTL 
    *pTTL = echoReply.ipHdr.TTL; 
    //返回应答时间
    return(echoReply.echoRequest.dwTime); 
} 

//等待回应答复 ,使用select模型
int WaitForReply(SOCKET s) 
{
    struct timeval timeout;
    fd_set readfds;
    readfds.fd_count = 1;
    readfds.fd_array[0] = s;
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    return(select(1, &readfds, NULL, NULL, &timeout));
} 

//PING
void Ping(char *pstrHost,bool logic) 
{ 
    char c; 
    SOCKET rawSocket; 
    LPHOSTENT lpHost; 
    struct sockaddr_in destIP; 
    struct sockaddr_in srcIP; 
    DWORD dwTimeSent; 
    DWORD dwElapsed; 
    unsigned char cTTL; 
    int nLoop,k=4; 
    int nRet,minimum=100000,maximum=0,average=0; 
    int sent=4,reveived=0,lost=0; 
    //创建原始套接字 ,ICMP 类型
	//原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧(数据包)。另外,必须在管理员权限下才能使用原始套接字。
    rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    // 第二个注释函数 socket 
    if (rawSocket == SOCKET_ERROR) 
    { 
        printf("原始套接字创建失败\t error:%d\n", WSAGetLastError()); 
        return; 
    } 
    //检测目标主机
    lpHost = gethostbyname(pstrHost); 
    if (lpHost==NULL) 
    { 
        printf("没有找到目标主机:%s\n", pstrHost); 
        return; 
    } 
    //设置目标机地址 
    destIP.sin_addr.s_addr = *((u_long FAR*)(lpHost->h_addr)); // 设置目标 IP 
    destIP.sin_family = AF_INET; //地址规格
    destIP.sin_port = 0; 
    //提示开始进行 PING 
    printf("\n正在Ping %s [%s] 具有%d字节的数据......\n",pstrHost,inet_ntoa(destIP.sin_addr),DATASIZE);
    //发起多次 PING 测试
    for (nLoop=0; nLoop<k; nLoop++){ 
        if (logic) k=k+1; 
        //发送 ICMP 回应请求 
        SendRequest(rawSocket, &destIP); 
        //等待回复的数据
        nRet = WaitForReply(rawSocket); 
        if(nRet == SOCKET_ERROR) 
        { 
            printf("select() error:%d\n", WSAGetLastError()); 
            break; 
        } 
        if (!nRet) 
        { 
            lost++; 
            printf("\n请求超时!"); 
            continue; 
        } 
        //接收回复
        dwTimeSent = RecvReply(rawSocket, &srcIP, &cTTL); 
        reveived++; 
        //计算花费的时间
        dwElapsed = GetTickCount() - dwTimeSent; 
        if(dwElapsed>maximum) maximum=dwElapsed; 
        if(dwElapsed<minimum) minimum=dwElapsed; 
        average+=dwElapsed; 
        printf("\n来自%s的回复-->\t字节:%d\t时间:%ldms\tTTL:%d", 
            inet_ntoa(srcIP.sin_addr),DATASIZE,dwElapsed,cTTL); 
        if(_kbhit()) // Use _getch to throw key away.  
        { 
            if ((c=_getch())==0x2) //crrl -b 
            break; 
        } else 
        Sleep(1000); 
    } 
    printf("\n\n"); 
	printf("%s的Ping统计信息如下:\n",inet_ntoa(srcIP.sin_addr)); 
    printf("数据包: 已发送=%d\t已接收=%d\t丢失=%d\n", sent,reveived,lost,(float)(lost*1.0/sent)*100); 
    if(lost==0) 
    { 
        printf("往返时间如下:\n"); 
        printf("最短时间=%dms\t最长时间=%dms\t平均时间=%dms\n",minimum,maximum,average/sent); 
    } 
    printf("****************************************************"); 
    nRet = closesocket(rawSocket); 
    if (nRet == SOCKET_ERROR) 
    { 
        printf("closesocket() error:%d\n", WSAGetLastError()); 
    } 
} 

//主程序
void main() 
{ 
	printf("*********************WelcometothePingTest*********************\n");
 printf("*************************Time:2018.11.15*************************\n");
    while(1) 
    { 
        WSADATA wsd;// 检测输入的参数 
        //初始化 Winsock 
        if(WSAStartup(MAKEWORD(1, 1), &wsd) != 0){// 第一个函数说明 WSAStartup() 
            printf(" 加载 Winsock 失败 !\n"); 
        } 
        char opt1[100]; 
        char *ptr=opt1; 
        bool log=false; 
        printf("\n\n请输入ip地址/域名:"); 
        cin.getline(opt1,100,'\n');//ping 的地址 字符串
        if(strstr(opt1, "-t")!=NULL) 
        { 
            log=true; 
            strncpy(ptr,opt1+0,strlen(opt1)-3);// 把原字符串的最后三位截取 
            ptr[strlen(opt1)-2]=0; 
            //printf("%s", ptr); 
        } 
        //开始 PING 
        Ping(ptr,log); 
        //程序释放 Winsock 资源
        WSACleanup(); 
    } 
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值