原始套接口的ping命令编程

本文介绍如何使用原始套接口直接操作IP或ICMP协议,通过创建、配置和发送ICMP包实现经典的PING命令。文章详细阐述了ICMP头部结构、发送与接收流程,以及超时设置等关键步骤。

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

原始套接口(SOCK_RAW)允许对较低层协议(如IP或ICMP)进行直接访问,常用于检验新的网络协议实现,也可用于测试新配置或安装的网络设备。创建一个原始套接口时,一般格式如下: 

SOCKET sockid=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);//ICMP协议
SOCKET sockid=socket(AF_INET,SOCK_RAW,IPPROTO_IP);//IP协议

以下是利用ICMP协议实现的PING命令:

 

 

#include <WINSOCK2.H>  
#pragma comment(lib,"ws2_32")  
#include <stdio.h>  
  
//--------------------①头部格式--------------  
/************************************************************************/  
/* IP头部                                                               */  
/************************************************************************/  
typedef struct IP_Header//20B的IP固定头部  
{  
    UCHAR ipVer;        //1B 4b版本号+4b头长度  
    UCHAR ipTOS;        //1B 服务类型  
    USHORT ipLength;    //2B 总长度(IP数据报的长度)  
    USHORT ipID;        //2B 标识(唯一标识发送的每一个数据报)  
    USHORT ipFlags;        //2B 3b标志+13b分段偏移  
    UCHAR ipTTL;        //1B 生存时间TTL  
    UCHAR ipProtocol;    //1B 上层协议标识(TCP、UDP、ICMP)      
    USHORT ipChecksum;    //2B 头部效验和  
    ULONG ipSource;        //4B 源IP地址  
    ULONG ipDestination;//4B 目的IP地址  
}IPHDR,*PIPHDR;  
/************************************************************************/  
/* ICMP头部                                                             */  
/************************************************************************/  
typedef struct ICMP_Header//8B的ICMP头部  
{  
    UCHAR icmp_type;    //1B 类型  
    UCHAR icmp_code;    //1B 代码  
    USHORT icmp_checksum;//2B 效验和  
    USHORT icmp_id;        //2B 标识符(通常设置为进程ID)  
    USHORT icmp_seq;    //2B 序号(0~3)  
}ICMPHDR,*PICMPHDR;  
//------------------------②函数声明-----------------------  
USHORT checksum(USHORT *buff,int size);  
int SetTimeout(SOCKET s,long nTime);  
//------------------------③main函数-----------------------  
int main()  
{  
    WSADATA wsaData;  
    if (WSAStartup(MAKEWORD(2,2),&wsaData)!=0)   
    {   
        printf("WSAStartup Failed,Error=【%d】\n",WSAGetLastError());   
        return 1;   
    }   
    else   
        printf("加载成功\n");   
    //----------创建原始套接字----------------  
    SOCKET sRaw=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);//创建原始套接字  
    //----------设置接收超时-----------------  
    struct timeval tv;  
    tv.tv_sec=1;  
    int F=SetTimeout(sRaw,tv.tv_sec);  
    if (F)  
    {  
        printf("setsockopt() Failed,Error=%d\n",WSAGetLastError());  
        return 1;  
    }  
    else  
        printf("设置接收超时为%d秒\n",tv.tv_sec);  
    //----------绑定-------------  
    struct sockaddr_in Sadd;  
    Sadd.sin_family=AF_INET;  
    Sadd.sin_port=htons(1111);  
    Sadd.sin_addr.s_addr=inet_addr("192.168.31.1");  
    if (bind(sRaw,(sockaddr*)&Sadd,sizeof(Sadd))==SOCKET_ERROR)  
    {  
        printf("bind() Failed,Error=%d\n",WSAGetLastError());  
        return 1;  
    }  
    else  
        printf("绑定成功,本地IP地址:【%s】,端口号:【%d】\n",inet_ntoa(Sadd.sin_addr),ntohs(Sadd.sin_port));  
    //---------目的IP地址(要ping的IP地址)--------------  
    struct sockaddr_in Dest;  
    Dest.sin_family=AF_INET;  
    Dest.sin_addr.s_addr=inet_addr("192.168.32.1");  
    //----------创建ICMP包----------------  
    char buff[sizeof(ICMPHDR)];        //char buff[sizeof(ICMPHDR)+32];  
    PICMPHDR pIcmp=(ICMPHDR*)buff;  
    // 1)填写ICMP包(公共部分)  
    pIcmp->icmp_type=8;  
    pIcmp->icmp_code=0;  
    pIcmp->icmp_id=(USHORT)GetCurrentProcessId();    //当前进程ID  
    //memset(&buff[sizeof(ICMPHDR)],'E',32);  
    //----------开始准备发送和接收ICMP封包--------------  
    char recvBuf[200];  
    struct sockaddr_in R_Dest;  
    int L_R_Dest=sizeof(struct sockaddr_in);  
    while (true)  
    {  
        static USHORT nSeq=0;    //seq由0开始  
        static int nCount=0;    //发送4个包  
        if (nCount++==4)  
            break;  
        //2)继续填写ICMP封包数据(可变部分)  
        pIcmp->icmp_checksum=0;  
        pIcmp->icmp_seq=nSeq++;  
        pIcmp->icmp_checksum=checksum((USHORT*)buff,sizeof(ICMPHDR));    //效验  
        //pIcmp->icmp_checksum=checksum((USHORT*)buff,sizeof(ICMPHDR)+32);  
        //-------------发送-------------  
        int time_send=GetTickCount();  
        int nRet=sendto(sRaw,buff,sizeof(ICMPHDR),0,(SOCKADDR*)&Dest,sizeof(Dest));  
        //int nRet=sendto(sRaw,buff,sizeof(ICMPHDR)+32,0,(SOCKADDR*)&Dest,sizeof(Dest));  
        if (nRet==SOCKET_ERROR)  
        {  
            printf("sendto() Failed,Error=%d\n",WSAGetLastError());  
            return 1;  
        }  
        //-------------接收---------------  
        int RRet=recvfrom(sRaw,recvBuf,sizeof(recvBuf),0,(sockaddr*)&R_Dest,&L_R_Dest);  
        int time_recv=GetTickCount();  
        if (nRet==SOCKET_ERROR)  
        {  
            if (WSAGetLastError()==WSAETIMEDOUT)  
            {  
                printf("timed out\n");  
                continue;  
            }  
            printf("recvfrom() Failed,Error=%d\n",WSAGetLastError());  
            return 1;  
        }  
        //------------解析----------------  
        PICMPHDR pRecvIcmp=(ICMPHDR*)(recvBuf+sizeof(IPHDR));  
        if (RRet<(nRet+sizeof(IPHDR)))  
            printf("从%s收到的数据过少\n",inet_ntoa(R_Dest.sin_addr));  
        if (pRecvIcmp->icmp_type!=0)    //不是应答  
        {  
            printf("这不是我要接收的应答报文,该报文类型为%d\n",pRecvIcmp->icmp_type);  
            return 1;  
        }  
        if (pRecvIcmp->icmp_id!=GetCurrentProcessId())  
        {  
            printf("这是别人的包\n");  
            return 1;  
        }  
        printf("%d bytes from %s,icmp_seq=%d,time:%d ms\n",RRet,inet_ntoa(R_Dest.sin_addr),pRecvIcmp->icmp_seq,time_recv-time_send);  
        Sleep(1000);  
    }  
    closesocket(sRaw);  
    WSACleanup();  
    return 0;  
}  
//---------------④效验函数-----------------  
USHORT checksum(USHORT *buff,int size)  
{  
    unsigned long cksum=0;  
    while (size>1)  
    {  
        cksum+=*buff++;  
        size-=2;  
    }  
    if (size==1)  
        cksum+=*(UCHAR*)buff;  
    cksum=(cksum>>16)+(cksum&0xffff);  
    cksum+=(cksum>>16);  
    USHORT ans=(USHORT)~cksum;  
    return (ans);  
}  
//---------------⑤接收超时函数---------------  
int SetTimeout(SOCKET s,long nTime)  
{  
    int ret=setsockopt(s,SOL_SOCKET,SO_RCVTIMEO,(char*)&nTime,sizeof(nTime));  
    return ret;  
}  

运行程序效果如下:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值