ping程序的实现

最近在看些Windows下网络编程问题。看到原始套接字的使用,于是看了Ping程序的功能实现。

大部分人用ping命令只是作为查看另一个系统的网络连接是否正常的一种简单方法。这里我介绍下在Windows下实现ping程序的两种方法。

一是使用原始套接字的方法:

代码如下:

  1. #include<Winsock2.h>
  2. #pragmacomment(lib,"ws2_32.lib")
  3. #pragmacomment(lib,"Iphlpapi.lib")
  4. typedefstructicmp_hdr
  5. {
  6. unsignedcharicmp_type;
  7. unsignedcharicmp_code;
  8. unsignedshorticmp_checksum;
  9. //回显头
  10. unsignedshorticmp_id;
  11. unsignedshorticmp_sequence;
  12. unsignedlongicmp_timestamp;
  13. }ICMP_HDR,*PICMP_HDR;
  14. typedefstruct_IPHeader//20字节的IP头
  15. {
  16. UCHARiphVerLen;//版本号和头长度(各占4位)
  17. UCHARipTOS;//服务类型
  18. USHORTipLength;//封包总长度,即整个IP报的长度
  19. USHORTipID;//封包标识,惟一标识发送的每一个数据报
  20. USHORTipFlags;//标志
  21. UCHARipTTL;//生存时间,就是TTL
  22. UCHARipProtocol;//协议,可能是TCP、UDP、ICMP等
  23. USHORTipChecksum;//校验和
  24. ULONGipSource;//源IP地址
  25. ULONGipDestination;//目标IP地址
  26. }IPHeader,*PIPHeader;
  27. USHORTchecksum(USHORT*buffer,intsize)
  28. {
  29. unsignedlongcksum=0;
  30. while(size>1)
  31. {
  32. cksum+=*buffer++;
  33. size-=sizeof(USHORT);
  34. }
  35. if(size)
  36. {
  37. cksum+=*(UCHAR*)buffer;
  38. }
  39. cksum=(cksum>>16)+(cksum&0xffff);
  40. cksum+=(cksum>>16);
  41. return(USHORT)(~cksum);
  42. }
  43. BOOLSetTimeout(SOCKETs,intnTime,BOOLbRecv)
  44. {
  45. intret=::setsockopt(s,SOL_SOCKET,
  46. bRecv?SO_RCVTIMEO:SO_SNDTIMEO,(char*)&nTime,sizeof(nTime));
  47. returnret!=SOCKET_ERROR;
  48. }
  49. intmain(intargc,char*argv[])
  50. {
  51. if(argc<2)
  52. {
  53. printf("Usage:PingIPaddress.\n");
  54. return-1;
  55. }
  56. charszDestIp[20];
  57. strcpy_s(szDestIp,argv[1]);
  58. //初始化套接字
  59. WSADATAwsaData;
  60. intwsaset=WSAStartup(0x101,&wsaData);
  61. SOCKETsRaw=::socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
  62. inta=::GetLastError();
  63. SetTimeout(sRaw,1000,TRUE);
  64. SOCKADDR_INdest;
  65. dest.sin_family=AF_INET;
  66. dest.sin_port=htons(0);
  67. dest.sin_addr.S_un.S_addr=inet_addr(szDestIp);
  68. //创建ICMP封包
  69. charbuff[sizeof(ICMP_HDR)+32];
  70. ICMP_HDR*pIcmp=(ICMP_HDR*)buff;
  71. //填写ICMP包
  72. pIcmp->icmp_type=8;
  73. pIcmp->icmp_code=0;
  74. pIcmp->icmp_id=(USHORT)::GetCurrentProcessId();
  75. pIcmp->icmp_checksum=0;
  76. pIcmp->icmp_sequence=0;
  77. //填充数据
  78. memset(&buff[sizeof(ICMP_HDR)],'E',32);
  79. //开始发送和接收ICMP封包
  80. USHORTnSeq=0;
  81. charrecvBuf[1024];
  82. SOCKADDR_INfrom;
  83. intnLen=sizeof(from);
  84. while(TRUE)
  85. {
  86. staticintnCount=0;
  87. intnRet;
  88. if(nCount++==4)
  89. break;
  90. pIcmp->icmp_checksum=0;
  91. pIcmp->icmp_timestamp=::GetTickCount();
  92. pIcmp->icmp_sequence=nSeq++;
  93. pIcmp->icmp_checksum=checksum((USHORT*)buff,sizeof(ICMP_HDR)+32);
  94. //发送
  95. nRet=::sendto(sRaw,buff,sizeof(ICMP_HDR)+32,0,(SOCKADDR*)&dest,sizeof(dest));
  96. if(nRet==SOCKET_ERROR)
  97. {
  98. printf("sendto()failed:%d\n",::WSAGetLastError());
  99. return-1;
  100. }
  101. nRet=::recvfrom(sRaw,recvBuf,1024,0,(sockaddr*)&from,&nLen);
  102. if(nRet==SOCKET_ERROR)
  103. {
  104. if(::WSAGetLastError()==WSAETIMEDOUT)
  105. {
  106. printf("timedout\n");
  107. continue;
  108. }
  109. printf("recvfrom()failed:%d\n",::WSAGetLastError());
  110. return-1;
  111. }
  112. //下面开始解析接收到的ICMP封包
  113. intnTick=::GetTickCount();
  114. if(nRet<sizeof(IPHeader)+sizeof(ICMP_HDR))
  115. {
  116. printf("Toofewbytesfrom%s\n",::inet_ntoa(from.sin_addr));
  117. }
  118. //接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头
  119. ICMP_HDR*pRecvIcmp=(ICMP_HDR*)(recvBuf+20);//(ICMP_HDR*)(recvBuf+sizeof(IPHeader));
  120. if(pRecvIcmp->icmp_type!=0)//回显
  121. {
  122. printf("nonechotype%drecvd\n",pRecvIcmp->icmp_type);
  123. return-1;
  124. }
  125. if(pRecvIcmp->icmp_id!=::GetCurrentProcessId())
  126. {
  127. printf("someoneelse'spacket!\n");
  128. return-1;
  129. }
  130. printf("%dbytesfrom%s:",nRet,inet_ntoa(from.sin_addr));
  131. printf("icmp_seq=%d.",pRecvIcmp->icmp_sequence);
  132. printf("time:%dms",nTick-pRecvIcmp->icmp_timestamp);
  133. printf("\n");
  134. ::Sleep(1000);
  135. }
  136. system("pause");
  137. return0;
  138. }

这是使用了原始套接字,实现发送ICMP数据包,实现ping程序。

在Linux中的Ping使用了这种方式。

这里是一个比较完整的关于Ping程序介绍的文章:http://www.ibm.com/developerworks/cn/linux/network/ping/index.html

这是Windows下实现的Ping程序:http://blog.csdn.net/tancfjob/archive/2008/05/07/2408978.aspx

但是在使用Windows下实现Ping的时候发现使用原始套接字需要管理员权限,但是Windows下使用ping程序并不需要使用管理员权限。感谢shadowstar 提示了我使用IcmpSendEcho来实现功能。

下面是使用IcmpSendEcho的示例代码:

  1. #pragmacomment(lib,"ws2_32.lib")
  2. #pragmacomment(lib,"Iphlpapi.lib")
  3. int__cdeclmain(intargc,char**argv){
  4. //Declareandinitializevariables
  5. HANDLEhIcmpFile;
  6. unsignedlongipaddr=INADDR_NONE;
  7. DWORDdwRetVal=0;
  8. charSendData[]="DataBuffer";
  9. LPVOIDReplyBuffer=NULL;
  10. DWORDReplySize=0;
  11. //Validatetheparameters
  12. if(argc!=2){
  13. printf("usage:%sIPaddress\n",argv[0]);
  14. return1;
  15. }
  16. ipaddr=inet_addr(argv[1]);
  17. if(ipaddr==INADDR_NONE){
  18. printf("usage:%sIPaddress\n",argv[0]);
  19. return1;
  20. }
  21. hIcmpFile=IcmpCreateFile();
  22. if(hIcmpFile==INVALID_HANDLE_VALUE){
  23. printf("\tUnabletoopenhandle.\n");
  24. printf("IcmpCreatefilereturnederror:%ld\n",GetLastError());
  25. return1;
  26. }
  27. ReplySize=sizeof(ICMP_ECHO_REPLY)+sizeof(SendData);
  28. ReplyBuffer=(VOID*)malloc(ReplySize);
  29. if(ReplyBuffer==NULL){
  30. printf("\tUnabletoallocatememory\n");
  31. return1;
  32. }
  33. dwRetVal=IcmpSendEcho(hIcmpFile,ipaddr,SendData,sizeof(SendData),
  34. NULL,ReplyBuffer,ReplySize,1000);
  35. if(dwRetVal!=0){
  36. PICMP_ECHO_REPLYpEchoReply=(PICMP_ECHO_REPLY)ReplyBuffer;
  37. structin_addrReplyAddr;
  38. ReplyAddr.S_un.S_addr=pEchoReply->Address;
  39. printf("\tSenticmpmessageto%s\n",argv[1]);
  40. if(dwRetVal>1){
  41. printf("\tReceived%ldicmpmessageresponses\n",dwRetVal);
  42. printf("\tInformationfromthefirstresponse:\n");
  43. }
  44. else{
  45. printf("\tReceived%ldicmpmessageresponse\n",dwRetVal);
  46. printf("\tInformationfromthisresponse:\n");
  47. }
  48. printf("\tReceivedfrom%s\n",inet_ntoa(ReplyAddr));
  49. printf("\tStatus=%ld\n",
  50. pEchoReply->Status);
  51. printf("\tRoundtriptime=%ldmilliseconds\n",
  52. pEchoReply->RoundTripTime);
  53. }
  54. else{
  55. printf("\tCalltoIcmpSendEchofailed.\n");
  56. printf("\tIcmpSendEchoreturnederror:%ld\n",GetLastError());
  57. return1;
  58. }
  59. return0;
  60. }

此段代码需要ws2_32.lib和Iphlpapi.lib,包含头文件iphlpapi.h和icmpapi.h

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值