c语言实现ping
2016-11-25 16:54
260 查看
在dos输入ping+主机名可以知道主机是否可达,这是网际控制报文协议ICMP的一个应用,为了更有效的转发IP数据报和提高交付成功机会,封装在ip数据包中的ICMP允许主机或路由器报告差错情况和提供异常情况的报告。更具体点的,向目的主机发送ICMP回送请求报文,收到此报文的主机必须向源主机或路由器发送ICMP回送回答报文。ICMP的报文格式是,常见的ICMP报文类型如下图
要模拟ping的过程,就是先数据封装一个ICMP报文,发送并对接收的ip数据报文处理的过程。以下为代码
要模拟ping的过程,就是先数据封装一个ICMP报文,发送并对接收的ip数据报文处理的过程。以下为代码
#include<stdio.h> #include<Winsock2.h> #include<ws2tcpip.h> #include<stdlib.h> #include<malloc.h> #include<string.h> #pragma comment("WS2_32.lib") #define DEF_ICMP_TIMEOUT 3000 //定义超时为3秒 #define ICMP_ECHO_REQUEST 8 //定义回显请求类型 #define DEF_ICMP_DATA_SIZE 20 //定义发送数据长度 #define DEF_ICMP_PACK_SIZE 32 //定义数据包长度 #define MAX_ICMP_PACKET_SIZE 1024 //定义最大数据包长度 #define ICMP_ECHO_REPLY 0 //定义回显应答类型 /* *IP报头结构 */ typedef struct { byte h_len_ver ; //IP版本号 byte tos ; // 服务类型 unsigned short total_len ; //IP包总长度 unsigned short ident ; // 标识 unsigned short frag_and_flags ; //标志位 byte ttl ; //生存时间 byte proto ; //协议 unsigned short cksum ; //IP首部校验和 unsigned long sourceIP ; //源IP地址 unsigned long destIP ; //目的IP地址 } IP_HEADER ; /* *定义ICMP数据类型 */ typedef struct { byte type ; //类型-----8 byte code ; //代码-----8 unsigned short cksum ; //校验和------16 unsigned short id ; //标识符-------16 unsigned short seq ; //序列号------16 unsigned int choose ; //选项-------32 } ICMP_HEADER ; typedef struct { int usSeqNo ; //记录序列号 DWORD dwRoundTripTime ; //记录当前时间 byte ttl ; //生存时间 IN_ADDR dwIPaddr ; //源IP地址 } DECODE_RESULT ; //将命令行参数转换为ip地址 unsigned long GetIp(char *str){ unsigned long ulDestIP=inet_addr(str); if(ulDestIP==INADDR_NONE){ HOSTENT *pHostent=gethostbyname(str); if(pHostent){ ulDestIP=(*(IN_ADDR*)pHostent->h_addr).s_addr; } pHostent=gethostbyname("www.baidu.com"); if (pHostent == NULL) { perror("getaddrinfo"); ulDestIP=2; } } return ulDestIP; } //产生网际校验和 unsigned short GenerateChecksum(unsigned short *pBuf,int iSize){ unsigned long cksum=0; while(iSize>1){ cksum+=*pBuf++;//将待校验的数据每16位逐位相加保存在cksum中 iSize-=sizeof(unsigned short);//每16位加完则将带校验数据量减16 } // if(iSize){ cksum+=*(unsigned char *)pBuf; } cksum=(cksum>>16)+(cksum&0xffff); cksum+=(cksum>>16); return (unsigned short)(~cksum); } //解析应答信息 int DecodeIcmpResponse(char *pBuf,int iPacketSize,DECODE_RESULT *stDecodeResult) { //获取收到的IP数据包的首部信息 IP_HEADER *pIpHrd=(IP_HEADER *)pBuf; int iIpHrdLen=20; if(iPacketSize<(int)(iIpHrdLen+sizeof(ICMP_HEADER))){ return 0; } //指针指向ICMP报文的首地址 ICMP_HEADER *pIcmpHrd=(ICMP_HEADER *)(pBuf+iIpHrdLen); unsigned short usID,usSquNo; //获取的数据包的type字段为ICMP_ECHO_REPLY,即受到一个回显应答的报文 if(pIcmpHrd->type==ICMP_ECHO_REPLY){ usID=pIcmpHrd->id; //接受到的是网络字节顺序的seq字段信息,需要转成主机字节顺序 usSquNo=ntohs(pIcmpHrd->seq); } if(usID!=GetCurrentProcessId()||usSquNo!=stDecodeResult->usSeqNo){ return 0; } if(pIcmpHrd->type==ICMP_ECHO_REPLY){ stDecodeResult->ttl=pIpHrd->ttl; stDecodeResult->dwIPaddr.s_addr=pIpHrd->sourceIP; stDecodeResult->dwRoundTripTime=GetTickCount()-stDecodeResult->dwRoundTripTime; return 1; } return 0; } //填充目的的socket int CreateSocket(unsigned long ulDestIP){ //填充目的的socket SOCKADDR_IN destSockAddr; ZeroMemory(&destSockAddr,sizeof(SOCKADDR_IN)); destSockAddr.sin_family=AF_INET; destSockAddr.sin_addr.s_addr=ulDestIP; destSockAddr.sin_port = htons(0); //初始化WinSock WORD wVersionRequested = MAKEWORD(2,2); WSADATA wsaData; if(WSAStartup(wVersionRequested,&wsaData) != 0) { printf("初始化WinSock失败!\n") ; return ; } //使用ICMP协议创建Raw Socket SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED); if(sockRaw == INVALID_SOCKET) { printf("WSASocket() failed: %d\n", WSAGetLastError()); return ; } //设置端口属性 int iTimeout=DEF_ICMP_TIMEOUT; if(setsockopt(sockRaw,SOL_SOCKET, SO_RCVTIMEO,(char *)&iTimeout,sizeof(iTimeout))==SOCKET_ERROR){ printf("设置SO_RCVTIMEO失败\n"); } if(setsockopt(sockRaw,SOL_SOCKET, SO_SNDTIMEO,(char *)&iTimeout,sizeof(iTimeout))==SOCKET_ERROR){ printf("设置SO_SNDTIMEO失败\n"); } //定义发送的数据段 char IcmpSendBuf[DEF_ICMP_PACK_SIZE] ; //填充ICMP数据包各字段 ICMP_HEADER * pIcmpHeader=(ICMP_HEADER *)IcmpSendBuf; pIcmpHeader->type=ICMP_ECHO_REQUEST; pIcmpHeader->code=0; pIcmpHeader->id=(unsigned short)GetCurrentProcessId(); memset(IcmpSendBuf+sizeof(ICMP_HEADER),'E',DEF_ICMP_DATA_SIZE); //循环发送4个请求回显icmp数据包 DECODE_RESULT stDecodeResult; int usSeqNo; for(usSeqNo=0;usSeqNo<=3;usSeqNo++){ pIcmpHeader->seq=htons(usSeqNo); pIcmpHeader->cksum=0; pIcmpHeader->cksum=GenerateChecksum((unsigned short *)IcmpSendBuf,sizeof(ICMP_HEADER)+DEF_ICMP_DATA_SIZE); //记录序列号和当前时间 stDecodeResult.usSeqNo=usSeqNo; stDecodeResult.dwRoundTripTime=GetTickCount(); //发送ICMP的EchoRequest数据包 if(sendto(sockRaw,IcmpSendBuf,sizeof(IcmpSendBuf),0,(SOCKADDR*)&destSockAddr,sizeof(destSockAddr))==SOCKET_ERROR){ //如果主机不可达则直接退出 if(WSAGetLastError()==WSAEHOSTUNREACH){ return 0; } } SOCKADDR_IN from; int iFromLen=sizeof(from); int iReadLen; //定义接收的数据包 char IcmpRecvBuf[MAX_ICMP_PACKET_SIZE] ; while(1){ iReadLen=recvfrom(sockRaw,IcmpRecvBuf,MAX_ICMP_PACKET_SIZE,0,(SOCKADDR*)&from,&iFromLen); if(iReadLen!=SOCKET_ERROR){//正确接受到一个ICMP报文 if(DecodeIcmpResponse(IcmpRecvBuf , sizeof(IcmpRecvBuf) , &stDecodeResult)==1) { printf("来自 %s 的回复: 字节 = %d 时间 = %dms TTL = %d\n" , inet_ntoa(stDecodeResult.dwIPaddr) , iReadLen - 20,stDecodeResult.dwRoundTripTime ,stDecodeResult.ttl) ; } break ; }else if(WSAGetLastError()==WSAETIMEDOUT){//接受超时 printf("time out ! *****\n") ; break ; }else{ printf("发生未知错误!\n") ; break ; } } } printf("\nping complete.\n"); closesocket(sockRaw); WSACleanup(); return 0; } int main(int argc,char *argv[]){ char *arr=(char *)malloc(sizeof(char)); unsigned long ulDestIP;//ip地址 if(argv[1]!=NULL){ ulDestIP=GetIp(argv[1]); }else{ printf("请输入ip地址:"); scanf("%s",arr); ulDestIP=GetIp(arr); } CreateSocket(ulDestIP); }
相关文章推荐
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能(转)
- 在linux中用C语言实现ping命令的部分功能
- 用C语言实现Ping程序功能
- 【TCP/IP】C语言实现Ping小程序
- (转载)用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- ping命令的C语言实现(linux, IPv4,简单版)
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- linux下ping的C语言实现
- 用c语言实现ping
- 基于C语言实现的Ping程序
- Linux下用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 基于C语言实现的Ping程序
- 用C语言实现Ping程序功能
- 基于C语言与原始套接字实现Ping程序