网络程序之ping指令的实现

本文介绍了一个基于Windows平台的Ping指令实现方案,利用原始套接字层自定义IP及ICMP头部信息,并通过发送ICMP请求包检测目标主机的可达性。文中详细展示了代码实现过程,包括初始化、发送请求、接收回应等功能。

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

  网络程序之ping指令的实现

 

    ping是用于探测网络的连通性的一个程序.它在网络程序设计中主要是用到了原始套接字层.用RAW编程是一件很有意思的事情.因为这可以自已自定义IP地址,自定义数据结构.能做自已想做的事情.是SOCKET中最底层的编程技术.
    ping的原理其实就是自定义一个报文,发送到目地主机.然后等对方的返回信息.来确认网络是否连通.下面是代码:

//#define u_char unsigned char
//#define u_short unsigned short
#include<windows.h>
#define ICMP_ECHOREPLY 0
#define ICMP_ECHOREQ 8
//////////////////////////////////
typedef struct tagIPDR
{
 u_char VIHL;//版本和类型
 u_char TOS;//服务质量
 u_char shortTotLen;//长度
    u_char shortID;//编号
 u_char shortFlagOff;//分片,Fragment
 u_char TTL;//生存时间
 u_char Protocol;//协议类型
 u_short Checksum;//校验和
 struct in_addr iaSrc;//源地址
 struct in_addr isDet;//目的地址
}IPHDR,*PIPHDR;//RFC791 的IP协议头类型
////////////////////////////////////////
typedef struct tagICMP
{
 u_char Type;//类型
 u_char Code;//代号
 u_short  Checksum;//校验号
 u_short ID;//标识号
 u_short Seq;//列号
 char Data;//数据信息
}ICMPHDR,*PICMPHDR;//RFC 792 ICMP协议头
/////////////////////////////////////////
#define REQ_DATASIZE 32
typedef struct tagECHOREQUEST
{
 ICMPHDR icmpHdr;//ICMP协议头
 DWORD dwTime;//数据传输时间
 char cData[REQ_DATASIZE];//传输数据
}ECHOREQUEST,*PECHOREQUEST;//请求回传的数据长度
///////////////////////////////////////////////////
typedef struct tagECHOREPLY
{
 IPHDR ipHdr;
 ECHOREQUEST echoRequest;
 char cFiler[256];
}ECHOREPLY,*PECHOREPLY;//回送请求报文

#include<Winsock.h>
#include<stdlib.h>
#include<stdio.h>
DWORD time;
void Ping(LPCSTR pstrHost);//ping 指令函数;
void ReportError(LPCSTR psrtFrom);
int WaitForEchoReply(SOCKET s);
int SendEchoRequest(SOCKET,LPSOCKADDR_IN);
DWORD RecvEchoReply(SOCKET,LPSOCKADDR_IN,char*);
u_short in_cksum(u_short *addr,int len);
void main()
{
 WSADATA wsaData;
 WORD version=MAKEWORD(1,1);
 char pstrHost[20];
 if(WSAStartup(version,&wsaData))
 {
  printf("初始化协议栈错误/n");
  getchar();
  return ;
 }
 if(wsaData.wVersion!=version)
 {
        printf("不支持WIDOWS的套接字/n");
  getchar();
  return ;
 }
 bool flag=true;
 while(flag)
 {
  printf("Ping>:");
  scanf("%s",pstrHost);
  if(strcmp(pstrHost,"exit")==0)
   break;
  printf("*****************************/n");
  Ping(pstrHost);
 }
 WSACleanup();
}
void Ping(LPCSTR pstrHost)
{
 SOCKET rawSocket;
 LPHOSTENT  lpHost;
 struct sockaddr_in saDest;
 struct sockaddr_in saSrc;
 DWORD dwTimeSent;
 DWORD dwElapsed;
 u_char cTTL;
 int nLoop;
 int nRet;
 rawSocket=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
 if(rawSocket==SOCKET_ERROR)
 {
  ReportError("套接字");
  return;
 }
 lpHost=gethostbyname(pstrHost);
   if(lpHost==NULL)
 {
  printf("无法找到这主机[%s]/n",pstrHost);
  getchar();
  return;
 }
 saDest.sin_addr.s_addr=*((u_long FAR*)(lpHost->h_addr));
 saDest.sin_family=AF_INET;
 saDest.sin_port=0;
 printf("/n探测主机%s[%s]:%d字节/n",pstrHost,inet_ntoa(saDest.sin_addr),REQ_DATASIZE);
 for(nLoop=0;nLoop<4;nLoop++)
 {
  SendEchoRequest(rawSocket,&saDest);
  nRet=WaitForEchoReply(rawSocket);
  if(nRet==SOCKET_ERROR)
  {
   ReportError("选择");
   break;
  }
     if(!nRet)
  {
  printf("发送超时/n"); 
  break;
  }
  dwTimeSent=RecvEchoReply(rawSocket,&saSrc,(char*)&cTTL);
  dwElapsed=time-dwTimeSent;
     printf("来自主机[%s]响应,字节:%d 时间:%ld毫秒最大生存期:%d/n",inet_ntoa(saSrc.sin_addr),REQ_DATASIZE,dwElapsed,cTTL);
 }
       if(closesocket(rawSocket)==SOCKET_ERROR)
     ReportError("关闭套接字");
}
int SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr)
{
 static ECHOREQUEST echoReq;
 static nId=1;
 static nSeq=1;
 static 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]=nRet;
    echoReq.dwTime=GetTickCount();
 time=echoReq.dwTime;//记录发送时间
 echoReq.icmpHdr.Checksum=in_cksum((u_short *)&echoReq,sizeof(ECHOREQUEST));
 nRet=sendto(s,(LPCSTR)&echoReq,sizeof(ECHOREQUEST),0,(LPSOCKADDR)lpstToAddr,sizeof(SOCKADDR_IN));
 if(nRet==SOCKET_ERROR)
  ReportError("发送出错");
    return nRet;
}
void ReportError(LPCSTR lpStr)
{
 printf("%s发生错误,错误号:%d/n",lpStr,WSAGetLastError());
 getchar();
}
DWORD RecvEchoReply(SOCKET s,LPSOCKADDR_IN lpsaFrom,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)
  ReportError("接收");
 *pTTL=echoReply.ipHdr.TTL;
 return echoReply.echoRequest.dwTime;
}
int WaitForEchoReply(SOCKET s)
{
 struct timeval Timeout;
 fd_set readfds;
 readfds.fd_count=1;
 readfds.fd_array[0]=s;
 Timeout.tv_sec=5;
 Timeout.tv_usec=0;
 return select(1,&readfds,NULL,NULL,&Timeout);
}
u_short in_cksum(u_short *addr,int len)
{
 register int nleft=len;
    register u_short *w=addr;
 register u_short answer;
 register int sum=0;
 while(nleft>1)
 {
  sum+=*w++;
  nleft-=2;
 }
 if(nleft==1)
 {
  u_short u=0;
  *(u_char *)(&u)=*(u_char *)w;
  sum+=u;
 }
 sum=(sum>>16)+(sum&0xffff);
 sum+=(sum>>16);
 answer=~sum;
 return answer;
}
 
   用这个程序可以做为一个子模块来处理网络中故障结点发现.因为如果网络中一台主机ping不到的话我们可以认为连通这台机器的网络出了故障.(令主机接收ping时).这样不断输入子网中的IP就可以得到本网络中的连通性.
  同时也可以应用到网络主机探测中,可以通过分析本机IP来自动生局域网IP地址类别.然后再进行探测就可以得出本地网络的拓扑结构图了.
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值