Ping程序设计实现

由于IP层的协议是一种点对点的协议,而非端对端的协议,它提供无连接的数据报服务,没有端口的概念,因此很少使用bind()和connect()函数,如果用的话也仅是用于设置IP地址。用socket函数生成原始套接字后,即可用sendto()或sendmsg()函数发送数据和用函数recvfrom()或recvmsg()接收数据。

ping程序发ICMP响应请求给某一主机,该主机返回一个ICMP响应应答,程序收到应答则显示结果.为了完成这个功能,首先应创建协议为ICMP的原始套接字以接收/发送ICMP包,然后需要构造ICMP包,通过原始套接字发送给对方主机.

#include <stdio.h> 
#include <stdlib.h> 
#include <winsock.h> 
#include<conio.h> 
#pragma comment(lib, "ws2_32.lib")// 导入库文件 
#define ICMP_ECHOREPLY 0 //ICMP 回应答复 
#define ICMP_ECHOREQ 8 //ICMP 回应请求 
#define REQ_DATASIZE 32 // 请求数据报大小
#include <iostream>
using namespace std; 

//定义 IP 首部格式
typedef struct IPHeader 
{ 
    u_char VIHL; // 版本和首部长度 
    u_char ToS; //服务类型 
    u_short TotalLen; // 总长度 
    u_short ID; // 标识号 
    u_short Frag_Flags; //片偏移量 
    u_char TTL; // 生存时间 
    u_char Protocol; // 协议 
    u_short Checksum; //首部校验和
    struct in_addr SrcIP; // 源 IP 地址 
    struct in_addr DestIP; // 目的地址
}IPHDR, *PIPHDR; 

//定义 ICMP 首部格式
typedef struct ICMPHeader 
{ 
    u_char Type; //类型 
    u_char Code; //代码 
    u_short Checksum; //首部校验和 
    u_short ID; // 标识 
    u_short Seq; //序列号
    char Data; //数据
}ICMPHDR, *PICMPHDR; 

//定义 ICMP 回应请求
typedef struct ECHOREQUEST 
{ 
    ICMPHDR icmpHdr; 
    DWORD dwTime; 
    char cData[REQ_DATASIZE]; 
}ECHOREQUEST, *PECHOREQUEST; 

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

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

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

//接收应答回复并进行解析
DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_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("recvfrom() error:%d\n",WSAGetLastError()); 
    } 
    //记录返回的 TTL 
    *pTTL = echoReply.ipHdr.TTL; 
    //返回应答时间
    return(echoReply.echoRequest.dwTime); 
} 

//等待回应答复 ,使用 select 模型
int WaitForEchoReply(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; 
    u_char cTTL; 
    int nLoop,k=4; 
    int nRet,minimum=100000,maximum=0,average=0; 
    int sent=4,reveived=0,lost=0; 
    //创建原始套接字 ,ICMP 类型
    rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    // 第二个注释函数 socket 
    if (rawSocket == SOCKET_ERROR) 
    { 
        printf("socket() error:%d\n", WSAGetLastError()); 
        return; 
    } 
    //检测目标主机
    lpHost = gethostbyname(pstrHost); 
    if (lpHost==NULL) 
    { 
        printf("Host not found:%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("\nPinging %s [%s] with %d bytes of data:\n",pstrHost,inet_ntoa(destIP.sin_addr),REQ_DATASIZE);
    //发起多次 PING 测试
    for (nLoop=0; nLoop<k; nLoop++){ 
        if (logic) k=k+1; 
        //发送 ICMP 回应请求 
        SendEchoRequest(rawSocket, &destIP); 
        //等待回复的数据
        nRet = WaitForEchoReply(rawSocket); 
        if(nRet == SOCKET_ERROR) 
        { 
            printf("select() error:%d\n", WSAGetLastError()); 
            break; 
        } 
        if (!nRet) 
        { 
            lost++; 
            printf("\nRequest time out."); 
            continue; 
        } 
        //接收回复
        dwTimeSent = RecvEchoReply(rawSocket, &srcIP, &cTTL); 
        reveived++; 
        //计算花费的时间
        dwElapsed = GetTickCount() - dwTimeSent; 
        if(dwElapsed > maximum) maximum=dwElapsed; 
        if(dwElapsed < minimum) minimum=dwElapsed; 
        average+=dwElapsed; 
        printf("\nReply from %s: bytes = %d time = %ldms TTL = %d", 
            inet_ntoa(srcIP.sin_addr),REQ_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("Ping statistics for %s:\n",inet_ntoa(srcIP.sin_addr)); 
    printf(" Packets: Sent = %d, Received = %d, Lost = %d (%.f%% loss),\n", 
        sent,reveived,lost,(float)(lost*1.0/sent)*100); 
    if(lost==0) 
    { 
        printf("Approximate round trip times in milli-seconds:\n"); 
        printf(" Minimum = %dms, Maximum = %dms, Average = %dms\n",minimum,maximum,average/sent); 
    } 
    printf("\n\n"); 
    nRet = closesocket(rawSocket); 
    if (nRet == SOCKET_ERROR) 
    { 
        printf("closesocket() error:%d\n", WSAGetLastError()); 
    } 
} 

//主程序
void main() 
{ 
    printf("Welcome to the Ping Test\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("Ping "); 
        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(); 
    } 
} 

实现效果:
在这里插入图片描述

  • 11
    点赞
  • 115
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值