以前学习ICMP协议时候写的一个模仿windows自带的ping程序。程序没有经过仔细的测试,估计还有点问题,如果你发现程序中的BUG,请发一封email到 yzljlss#126.com (为防止垃圾邮件,@用#替换)告诉我。3Q。
/**/
/******************************************************************
程序: myping
功能: 模拟ping命令
O/S : WINDOWS 98或更高版本
作者: 严政
时间: 2007.8.14
版本变更:
V1.0.0 2007.8.14 初始版本,实现了基本的ping功能
说明:
这是开源代码,你可以随意拷贝使用。如果你有好的建议或
意见,发E-mail至: [ yzljlss@126.com ]讨论。
*******************************************************************/
#include
<
stdio.h
>
#include
<
WINSOCK2.H
>

#pragma
comment(lib,"wsock32.lib")
#pragma
comment(lib,"Ws2_32.lib")

#define
SIO_RECALL _WSAIOW(IOC_VENDOR,1)
#define
MAX_HOSTNAME_LEN 256

sockaddr_in LocalAddr,SendAddr,destAddr;
SOCKET sock;

struct
hostent FAR
*
pHostent,
*
pTmp;

int
total
=
4
;
//
发送ping报文次数,默认为4次
int
seconds
=
0
;
//
发送时间间隔
bool
hostToIp
=
false
;
//
-a 将目标的机器标识转换为ip地址
bool
pingforever
=
false
;
//
-t 若使用者不人为中断会不断的ping下去
bool
isCount
=
false
;
//
-c count 要求ping命令连续发送count个数据包
bool
isSimple
=
false
;
//
-q ping只在开始和结束时打印一些概要信息
char
FAR name[MAX_HOSTNAME_LEN];
char
destIP[
16
];
//
目标IP
typedef
struct
_ping

...
{
UCHAR i_type;//8位类型
UCHAR i_code;//8位代码
USHORT i_chksum;//16位ICMP校验和
USHORT i_identify;//16位标志位
USHORT i_seqnum;//16位序号
ULONG i_timestamp;//32位时间戳
UCHAR i_data[32];//32BYTE选项数据
}
PingHeader,
*
pPingHeader;

typedef
struct
_ipHeader
//
IP头部,总长度20字节

...
{
#if LITTLEENDIAN
UCHAR IpHlen:4, //4位首部长度
IpVer :4; //4位IP版本号
#else
UCHAR IpVer :4, //4位IP版本号
IpHlen:4; //4位首部长度
#endif
UCHAR IpTos;//8服务类型
USHORT IpTlen;//总长度
USHORT IpId;//标志
USHORT FlagsOff;//分片偏移
UCHAR IpTtl;//生存时间
UCHAR IpProto;//协议
USHORT ChkSum;//检验和
struct in_addr SourIp;//源IP地址
struct in_addr DestIp; //目的IP地址
}
IpHeader,
*
pIpHeader;

//
求校验和
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);
}

//
帮助信息
void
help()

...
{
printf("==========================[ myping V1.0.0 ]============================ ");
printf(" 用法: myping [-a] [-t] [-c count] [-i seconds] [-q] [-h] target_IP ");
printf("参数: ");
printf(" -a 将目标的机器标识转换为ip地址 ");
printf(" 建议: ping远程主机时不要添加此参数,否则速度较慢 ");
printf(" -t 若使用者不人为中断会不断的ping下去 ");
printf(" -c count 要求ping命令连续发送count个数据包 ");
printf(" -i seconds 在两次数据包发送之间间隔一定的秒数 ");
printf(" -q myping只在开始和结束时打印一些概要信息 ");
printf(" -h 帮助信息 ");
printf("例如: myping -a -i 1 -c 10 192.168.0.100 ");
printf("=========================[ By 严政 07.8.14 ]=========================== ");
}

//
发送,解析PING报文
int
funPing()

...
{
unsigned long i,
totalrecv=0;//收到包的数目
unsigned long addr, timestamp,
maxtime = 0,//最大延迟
mintime = 0;//最小延迟
PingHeader ping,*ping_hdr;
char recv_buff[65535];
char szDestIP[16];
int recvLen;

pIpHeader ip_hdr;

SendAddr.sin_family = AF_INET;
SendAddr.sin_addr.s_addr = inet_addr(destIP);
if(hostToIp)

...{
memset(name, 0, MAX_HOSTNAME_LEN);
//获取ping对象主机名
addr = inet_addr(destIP);
pHostent = gethostbyaddr((char *)&addr, sizeof(destIP) , AF_INET);
if(pHostent == NULL)

...{
//printf("fail to get host name: %d ",WSAGetLastError());
fprintf(stdout, "Ping %s with 32 bytes of datas: ",destIP);
}
else
fprintf(stdout, "Ping %s[ %s ] with 32 bytes of datas: ", pHostent->h_name, destIP);
}
else
fprintf(stdout, "Ping %s with 32 bytes of datas: ",destIP);
for(i=0;;i++)

...{
if(!pingforever)

...{
if(i >= (unsigned long)total)//达到发送次数
break;
}
//填充PING报文
ping.i_type = 8;
ping.i_code = 0;
ping.i_seqnum = (USHORT)i;
ping.i_identify = (unsigned short)GetCurrentProcessId();
ping.i_timestamp = (unsigned long)::GetTickCount();
for(int j=0;j < 32; j++)
ping.i_data[i] = (UCHAR)('a'+j);
ping.i_chksum = 0;
//计算校验和
ping.i_chksum = checksum((unsigned short*)&ping,sizeof(ping));
//printf("checksum=%d ",ping.i_chksum);
if(sendto(sock, (char*)&ping, sizeof(ping),0, (struct sockaddr*)&SendAddr, sizeof(SendAddr)) == SOCKET_ERROR)

...{
printf("Send ping packet error: %d ",WSAGetLastError());
return -1;
}
memset(recv_buff, 0, 1024);
int len = sizeof(destAddr);
if((recvLen = recvfrom(sock, recv_buff, sizeof(recv_buff), 0, (struct sockaddr*)&destAddr, &len)) == SOCKET_ERROR)

...{
int err = WSAGetLastError();
if(err != 10060)//超时错误不返回

...{
printf("recv data error: %d ",err);
return -1;
}
else if(!isSimple)
fprintf(stdout, "请求超时. ");
}
if(recvLen > 0)

...{
//处理接收的IP报文,解析PING应答报文
ip_hdr = (pIpHeader)recv_buff;

memcpy(szDestIP, inet_ntoa(ip_hdr->SourIp), 16);
if(ip_hdr->IpProto == IPPROTO_ICMP && !strcmp(szDestIP, destIP))//处理来自PING对象且是ICMP的报文

...{
ping_hdr = (pPingHeader)(recv_buff + sizeof(unsigned long)*ip_hdr->IpHlen);
// fprintf(stdout,"ping_hdr.i_type=%02X ",ping_hdr->i_type);
// fprintf(stdout,"ping_hdr.i_code=%02X ",ping_hdr->i_code);
// fprintf(stdout,"ping_hdr.i_seqnum=%04X ",ping_hdr->i_seqnum);
// fprintf(stdout,"ping_hdr.i_identify=%04X ",ping_hdr->i_identify);
// fprintf(stdout,"ping_hdr.timestamp=%08X ",ping_hdr->i_timestamp);
//应答报文
if(ping_hdr->i_type == 0)

...{
//计算延迟时间
timestamp = (unsigned long)::GetTickCount();
timestamp -= ping_hdr->i_timestamp;
if(i == 0)
mintime = timestamp;
maxtime = (timestamp > maxtime) ? timestamp : maxtime;//最大延迟时间
mintime = (timestamp < mintime) ? timestamp : mintime;//最小延迟时间

if(timestamp == 0)
timestamp = 1;
if(!isSimple)
fprintf(stdout, "Reply from %s: bytes=%d time<%dms TTL=%d ", destIP,
sizeof(ping_hdr->i_data), timestamp, ip_hdr->IpTtl );
//收到包的数目
totalrecv++;
}
if(ping_hdr->i_type == 3)

...{
fprintf(stdout, "目的不可达");
switch(ping_hdr->i_code)

...{
case 0:
fprintf(stdout, "(网络不可达) ");
break;
case 1:
fprintf(stdout, "(主机不可达) ");
break;
case 2:
fprintf(stdout, "(协议不可达) ");
break;
case 3:
fprintf(stdout, "(端口不可达) ");
break;
default:
break;
}
}
if(ping_hdr->i_type == 5)

...{
if(ping_hdr->i_code == 0)
fprintf(stdout, "对网络重定向. ");
if(ping_hdr->i_code == 1)
fprintf(stdout, "对主机重定向. ");
}
}
}
Sleep(seconds);
}//end for
//计算ping统计信息
fprintf(stdout, " Ping %s 的统计信息: ",destIP);
fprintf(stdout, " 包: 发送 = %d, 收到 = %d, 丢失 = %d (丢失率: %.0f%%) ",
i, totalrecv, (i-totalrecv), ((float)(i-totalrecv))/i*100 );
if(totalrecv != 0)//没收到包打印路由信息无意义

...{
fprintf(stdout, "近似路由时间(毫秒): ");
fprintf(stdout, " 最大 = %dms, 最小 = %dms, 平均 = %dms ", maxtime, mintime, (maxtime+mintime)/2);
}
return 0;
}

int
main(
int
argc,
char
*
argv[])

...
{
DWORD lpvBuffer = 1;
DWORD lpcbBytesReturned = 0;
int nNetTimeout=3000;//超时3秒
//初始化套结字
WSADATA WSAData;
if(WSAStartup(MAKEWORD(2,2), &WSAData) != 0)

...{
printf("fail to init socket: %d",WSAGetLastError());
return -1;
}
//创建套结字
sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&nNetTimeout,sizeof(int));

if(sock == SOCKET_ERROR)

...{
printf("fail to create socket: %d",WSAGetLastError());
return -1;
}
//获取本机IP
if(gethostname(name, MAX_HOSTNAME_LEN))

...{
printf("get host name error: %d",WSAGetLastError());
return -1;
}
pHostent = (struct hostent *)malloc(sizeof(struct hostent));
pTmp = pHostent;
pHostent = gethostbyname(name);
LocalAddr.sin_family = AF_INET;
LocalAddr.sin_port = htons(0);
memcpy(&LocalAddr.sin_addr.S_un.S_addr, pHostent->h_addr_list[0], pHostent->h_length);
//bind socket
if(bind(sock, (struct sockaddr *)&LocalAddr, sizeof(LocalAddr)) == SOCKET_ERROR)

...{
printf("bind error: %d",WSAGetLastError());
return -1;
}

/**//*
//置网卡为混杂模式
//在这个ping程序中,不能将网卡置于混杂模式,否则接收的报文包括自己发送的报文 (^_^)
if(WSAIoctl(sock, SIO_RECALL, &lpvBuffer ,sizeof(lpvBuffer), NULL, 0,
&lpcbBytesReturned, NULL, NULL) == SOCKET_ERROR)
{
printf("WSAIoctl(...) error: %d",WSAGetLastError());
return -1;
}
*/
//命令行解析
if(argc < 2)

...{
printf("必须输入ping参数! ");
help();
return -1;
}
else

...{
for(int i=1; i < argc; i++)

...{
if(argv[i][0] == '-')

...{
switch( (tolower(argv[i][1])) )

...{
case 'a':
hostToIp = true;
break;
case 't':
pingforever = true;
break;
case 'h':
help();
return -1;
break;
case 'q'://ping只在开始和结束时打印一些概要信息
isSimple = true;
break;
case 'c':

...{
if( *(argv[i]+3) > '9' || *(argv[i]+3) < '0' )

...{
printf("ping次数错误参数! ");
help();
return -1;
}
//发送报文次数
total = atoi(argv[++i]);
break;
}
case 'i'://设置发送报文时间差

...{
if( *(argv[i]+3) > '9' || *(argv[i]+3) < '0' )

...{
printf("时间错误参数! ");
help();
return -1;
}
//发送报文次数
seconds = atoi(argv[++i]) * 1000;
break;
}
default:
break;
}//end switch
}//end if
}//end for
if( (argv[argc-1][0]) > '9' || (argv[argc-1][0]) < '0' )

...{
printf("目的IP错误!请确认最后一个参数是目的IP. ");
help();
return -1;
}
memcpy(destIP, argv[argc-1], strlen(argv[argc-1]));
}
//发送ping报文
funPing();

free(pTmp);
closesocket(sock);
WSACleanup();

return 0;
}