用icmp实现traceroute功能 到最后一跳接收超时是什么问题?#include "stdio.h"
#include "WINSOCK2.h"
#include <ws2tcpip.h>
#include <string.h>
#pragma comment(lib,"wsock32.lib")
///////////////ICMP类型字段:
#define ICMP_ECHO       8                 //发送Ping请求时的ICMP报文类型
#define ICMP_ECHOREPLY  0                 //接收Ping回复时的ICMP报文类型
#define ICMP_TIMEOUT    11                //ICMP超时报文类型
#define ICMP_MIN        8                 //Minimum 8-byte ICMP packet(header)
#define MAX_PACKET      1024              //Max ICMP packet size
#define DEICMP_PACKSIZE 44                //Defaut ICMP PACKET SIZE
#define MAX_HOP 30//定义最大节点数
#define DEF_PACKET_SIZE 32//缺省数据报长度
#define ICMP_DESTUNREACH 3
//ip头部
typedef struct iphdr{
unsigned int h_len:4;     //头部长度
unsigned int version:4; //IP版本
unsigned char tos; //服务类型
unsigned short tatal_len; //数据包总长度
unsigned short ident; //唯一的标识
unsigned short farg_and_flags; //标志
unsigned char ttl; //生命周期
unsigned char proto; //协议类型
unsigned short checksum; //ip校验
unsigned int sourceIP; //源地址
unsigned int destIP; //目的地址
}IpHeader;
///////////////////////ICMP头部定
typedef struct 
{
BYTE   i_type;                        //报文类型
BYTE   i_code;                        //代码
USHORT i_cksum;                       //校验和
USHORT i_id;                          //标识符
USHORT i_seq;                         //序号
// ULONG 
ULONG timestamp;   //时间戳
} IcmpHeader;//解析返回数据包
int decode_resp(char *buf,int bytes,SOCKADDR_IN *from,int ttl);
//清除数据
void Cleanup();
//数据校验
USHORT checksum(USHORT *buffer,int size);
//设置ttl
int set_ttl(SOCKET s,int nTimeToLive);
//填充ICMP数据包
void fill_icmp_data(char *icmp_data,int datasize);
//连接到主机
void ConnectToHost(char* strHost);int main(){
SOCKET m_sockRaw=INVALID_SOCKET;
SOCKADDR_IN m_addrDest;
SOCKADDR_IN m_addrFrom;
int m_nTTL=1;
char * m_IcmpData=NULL;
char * m_RcvBuffer=NULL;
int m_nSeqno=0;///////////////////////////////////
int m_nTimeout = 1000;
BOOL m_bDone=FALSE; 
int m_nDatasize;
int m_nMaxhops=30;
//初始化 socket
WSADATA wsaData;
if(WSAStartup(MAKEWORD(2,2),&wsaData) !=0){
printf("WSAStartup failed!\n");
return 1;
} //创建socket
m_sockRaw = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,
0,WSA_FLAG_OVERLAPPED);
if(m_sockRaw == INVALID_SOCKET){
printf("WSASocket failed!\n");
return -1;
}
//设置发送和接收超时参数
int ret = setsockopt(m_sockRaw,SOL_SOCKET,SO_RCVTIMEO,
(char *)&m_nTimeout,sizeof(m_nTimeout));
if(ret == SOCKET_ERROR){
printf("设置接收超时参数失败!\n");
return -1;
}
ret = setsockopt(m_sockRaw,SOL_SOCKET,SO_SNDTIMEO,
(char*)&m_nTimeout,sizeof(m_nTimeout));
if(ret == SOCKET_ERROR){
printf("设置发送超时参数失败!\n");
return -1;
}
//解析地址
char strHost[]="202.114.18.190";

//printf("1111111111111111111111111111111111111111111111111");
m_addrDest.sin_family = AF_INET;
if((m_addrDest.sin_addr.s_addr=inet_addr(strHost))==INADDR_NONE){
HOSTENT * hp;
hp=gethostbyname(strHost);
if(hp)
memcpy(&(m_addrDest.sin_addr),hp->h_addr,hp->h_length);
else{
printf("输入的主机不存在!\n");
return 1;
}
}
////设置路由选项
int bOpt = TRUE;
if(setsockopt(m_sockRaw,SOL_SOCKET,SO_DONTROUTE,(char *)&bOpt,
sizeof(BOOL))==SOCKET_ERROR){
printf("设置socket参数失败!\n");
return 1;
}
m_nDatasize = DEF_PACKET_SIZE;
m_nDatasize+=sizeof(IcmpHeader);
//为icmp 数据包分配发送和接收缓冲区
m_IcmpData = (char *)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);
m_RcvBuffer = (char *)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);
if((!m_IcmpData)|(!m_RcvBuffer)){
printf("堆分配数据失败!\n");
return 1;
}
//创建icmp数据包
memset(m_IcmpData,0,MAX_PACKET);
printf("Tracing route to %s over a maximum of %d hops:\r\n",strHost,m_nMaxhops);
////fill_icmp_data(m_IcmpData,m_nDatasize);//////

IcmpHeader *icmp_hdr;
char *datapart;
icmp_hdr = (IcmpHeader*)m_IcmpData;
icmp_hdr->i_type = ICMP_ECHO;
icmp_hdr->i_code = 0;
icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
icmp_hdr->i_cksum=0;
icmp_hdr->i_seq = 0;
datapart = m_IcmpData+sizeof(IcmpHeader);
memset(datapart,'E',m_nDatasize-sizeof(IcmpHeader));
////////////////////////////////////
for(m_nTTL =1;((m_nTTL<=m_nMaxhops)&&(!m_bDone));m_nTTL++)
{
int bwrote;
//设置socket生命周期
//set_ttl(m_sockRaw,m_nTTL);/////////////////////
if((setsockopt(m_sockRaw,IPPROTO_IP,IP_TTL,(LPSTR)&m_nTTL,sizeof(int)))==SOCKET_ERROR)
 {
  printf("设置socket选项失败!");
  return 0;
 }
//往icmp头部添加信息
((IcmpHeader*)m_IcmpData)->i_cksum = 0;
((IcmpHeader*)m_IcmpData)->timestamp=GetTickCount(); ((IcmpHeader*)m_IcmpData)->i_seq=m_nSeqno++;
((IcmpHeader*)m_IcmpData)->i_cksum=checksum((USHORT*)m_IcmpData,
m_nDatasize);
//发送icmp包到目的端
bwrote = sendto(m_sockRaw,m_IcmpData,m_nDatasize,0,
(SOCKADDR*)&m_addrDest,sizeof(m_addrDest));
if(bwrote == SOCKET_ERROR)
{
if(WSAGetLastError()==WSAETIMEDOUT){
printf("%2d Send request timed out ./t/n",m_nTTL);
 continue ;
}
printf("发送数据函数出错!\r\n");
}
//从目的端读取数据报
int fromlen;
fromlen = sizeof(SOCKADDR);
ret = recvfrom(m_sockRaw,m_RcvBuffer,MAX_PACKET,0,(struct sockaddr*)&m_addrFrom,&fromlen);
if(ret == SOCKET_ERROR)
{
if(WSAGetLastError()==WSAETIMEDOUT){
printf("%2d Recvive request timed out .\t\n",m_nTTL);
continue ;
}
printf("接受数据函数出错!\r\n");
return -1;
}
//解析返回的icmp数据报的信息
m_bDone = decode_resp(m_RcvBuffer,ret,&m_addrFrom,m_nTTL);//////////////////
Sleep(1000);
}
getchar();
}void fill_icmp_data(char * icmp_data,int datasize){
IcmpHeader *icmp_hdr;
char *datapart;
icmp_hdr = (IcmpHeader*)icmp_data;
icmp_hdr->i_type = ICMP_ECHO;
icmp_hdr->i_code = 0;
icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
icmp_hdr->i_seq = 0;
datapart = icmp_data+sizeof(IcmpHeader);
memset(datapart,'E',datasize-sizeof(IcmpHeader));
}/*int set_ttl(SOCKET s,int nTimeToLive){
int nRet;
nRet = setsockopt(s,IPPROTO_IP,IP_TTL,(LPSTR)&nTimeToLive,
sizeof(int));
if(nRet == SOCKET_ERROR){
printf("设置socket选项失败!\n");
return 0 ;
}
return 1;
}*/USHORT checksum(USHORT *buffer,int size){
unsigned long cksum=0;
while(size>1){
cksum+=*buffer;
size-=sizeof(USHORT);
}
if(size)
cksum+=*(UCHAR*)buffer;
cksum = (cksum>>16)+(cksum&0xffff);
cksum+=(cksum>>16);
return(USHORT)(~cksum);
}int decode_resp(char *buf,int bytes,SOCKADDR_IN* from,int ttl)
{
IpHeader *iphdr = NULL;
IcmpHeader *icmphdr = NULL;
unsigned short iphdrlen;
struct hostent *IpHostent = NULL;
struct in_addr inaddr = from->sin_addr;
iphdr = (IpHeader*)buf;
iphdrlen = iphdr->h_len*4;
if(bytes<iphdrlen+ICMP_MIN){
printf("Too few bytes from %s\n",from->sin_addr);
}
icmphdr = (IcmpHeader*)(buf+iphdrlen); switch(icmphdr->i_type){
case ICMP_ECHOREPLY://目的地址返回的icmp
IpHostent = gethostbyaddr((const char*)&from->sin_addr,
AF_INET,sizeof(struct in_addr));
if(IpHostent!=NULL)
printf("%2d %s(%s)\r\n",ttl,IpHostent->h_name,inet_ntoa(inaddr));
return 1;break;
case ICMP_TIMEOUT://沿着这条链路返回的路由
printf("%2d %s\r\n",ttl,inet_ntoa(inaddr));
return 0;break;
case ICMP_DESTUNREACH://不能够到达目的的地址
printf("%2d %s reports:HOST is unreachable \r\n",ttl,inet_ntoa(inaddr));
return 1;
break;
default:
printf("non-echo type%d recvd\n",icmphdr->i_type);
return 1;
break;
} return 0;
}