tnblog
首页
视频
资源
登录

447行代码实现ping的功能

5295人阅读 2019/8/25 16:33 总访问:3467325 评论:7 收藏:1 手机
分类: 传说中的c
  1. /*
  2. 导入库文件
  3. */
  4. #pragma comment( lib,"ws2_32.lib")
  5. //加载头文件
  6. #include <WinSock2.h>
  7. #include <WS2tcpip.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <math.h>
  12. //定义常量
  13. //表示要记录的路由
  14. #define IP_RECORD_ROUTE 0x7
  15. //默认数据报的大小
  16. #define DEF_PACKET_SIZE 32
  17. //最大的ICMP数据报大小
  18. #define MAX_PACKET 1024
  19. //最大的ip头长度
  20. #define MAX_IP_HDR_SIZE 60
  21. //icmp报文类型,回显请求
  22. #define ICMP_ECHO 8
  23. //ICMP报文类型,回显应答
  24. #define ICMP_ECHO 0
  25. //最小的ICMP数据报大小
  26. #define ICMP_ECHOREPLY 8
  27. //自定义函数
  28. void InitPing();
  29. typedef struct _iphdr {
  30. unsigned int h_len : 4;   //IP报头的长度
  31. unsigned int version:4;  //IP的版本号
  32. unsigned char tos;  //服务类型
  33. unsigned short total_len;  //数据报的总长度
  34. unsigned short ident;  //唯一的标识符
  35. unsigned short frag_flags;  //分段标志
  36. unsigned char ttl;  //生存期
  37. unsigned char proto;  //协议类型(TCP,UDP)
  38. unsigned short checksum;  //校验和
  39. unsigned int sourceIP;  //源ip地址
  40. unsigned int destIP;  //目的ip地址
  41. }IpHeader;
  42. //ICMP报头的字段的数据结构
  43. typedef struct _icmphdr 
  44. {
  45. BYTE i_type;  //ICMP报文类型
  46. BYTE i_code;  //该类型中的代码号
  47. USHORT i_cksum;  //校验和
  48. USHORT i_id;  //唯一的标识符
  49. USHORT i_seq;  //序列号
  50. USHORT timestamp;  //时间戳
  51. }IcmpHeader;
  52. //IP选项头字段数据结构
  53. typedef struct _ipoptionhdr {
  54. unsigned char code;  //选项类型
  55. unsigned char len;  //选项头长度
  56. unsigned char ptr;  //地址偏移长度
  57. unsigned long addr[9];  //记录的ip地址列表
  58. }IpOptionHeader;
  59. //定义全局变量
  60. SOCKET m_socket;
  61. IpOptionHeader IpOption;
  62. SOCKADDR_IN DestAddr;
  63. SOCKADDR_IN SourceAddr;
  64. char* icmp_data;
  65. char* recvbuf;
  66. USHORT seq_no;
  67. char* lpdest;
  68. int datasize;
  69. BOOL RecordFlag;
  70. double PacketNum;
  71. BOOL SucessFlag;
  72. //下面开始函数
  73. //初始化函数
  74. void InitPing() 
  75. {
  76. WSADATA wsaData;
  77. icmp_data = NULL;
  78. seq_no = 0;
  79. recvbuf = NULL;
  80. RecordFlag = FALSE;
  81. lpdest = NULL;
  82. datasize = DEF_PACKET_SIZE;
  83. PacketNum = 5;
  84. SucessFlag = FALSE;
  85. //winsock初始化 WSAAccept 对winsocket进行加载
  86. if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
  87. {
  88. //如果初始化不成功则报错
  89. printf("WSAStartup() failed: %d\n",GetLastError());
  90. return;
  91. }
  92. m_socket = INVALID_SOCKET;
  93. }
  94. /*显示信息函数*/
  95. void UserHelp() {
  96. printf("UserHelp: ping -r <host> [data size] \n");
  97. printf("               -r        record route \n");
  98. printf("               -n        record amount \n");
  99. printf("               host        remote machine to ping \n");
  100. printf("               datasize        can be up to 1KB\n");
  101. }
  102. /*获取ping选项函数*/
  103. void GetArgments(int argc,char** argv) {
  104. int i;
  105. int j;
  106. int exp;
  107. int len;
  108. int m;
  109. //如果没有指定目的地址和任何选项
  110. if (argc==1)
  111. {
  112. printf("\nPlease specify the destination IP address and the ping option as follow!\n");
  113. for (i = 0; i < argc; i++)
  114. {
  115. len = strlen(argv[i]);
  116. if (argv[i][0] == '-')
  117. {
  118. //选项指令要获取记录条数
  119. if (isdigit(argv[i][1]))
  120. {
  121. PacketNum = 0;
  122. for (j = len-1,exp=0; j >= 1; j--,exp++)
  123. {
  124. //根据argv[i][j]中的ASCII值计算要获取的记录条数(十进制数)
  125. PacketNum += ((double)(argv[i][j] - 48)) * pow(10exp);
  126. }
  127. }
  128. else
  129. {
  130. switch (tolower(argv[i][1]))
  131. {
  132. //选项指令要获取路由信息
  133. case 'r':
  134. RecordFlag = TRUE;
  135. break;
  136. //没有按要求提供选项
  137. default:
  138. UserHelp();
  139. break;
  140. }
  141. }
  142. }
  143. //参数是数据报大小或者IP地址
  144. else if(isdigit(argv[i][0]))
  145. {
  146. for (m = 0; m < len; m++)
  147. {
  148. if (!isdigit(argv[i][m]))
  149. {
  150. //是IP地址
  151. lpdest = argv[i];
  152. break;
  153. }
  154. //是数据报大小
  155. else if (m == len - 1)
  156. datasize = atoi(argv[i]);
  157. }
  158. }
  159. //参数是主机名
  160. else
  161. {
  162. lpdest = argv[i];
  163. }
  164. }
  165. }
  166. }
  167. //求校验和函数
  168. USHORT CheckSum(USHORT* buffer,int size) 
  169. {
  170. unsigned long cksum = 0;
  171. while (size>1)
  172. {
  173. cksum += *buffer++;
  174. size -= sizeof(USHORT);
  175. }
  176. if (size)
  177. {
  178. cksum += *(UCHAR*)buffer;
  179. }
  180. //每16位取二进制反码求和
  181. cksum = (cksum >> 16) + (cksum & 0xffff);
  182. cksum += (cksum >> 16);
  183. return (USHORT)(~cksum);
  184. }
  185. //填充ICMP数据报字段函数
  186. void FillICMPData(char *icmp_data,int datasize) 
  187. {
  188. IcmpHeader* icmp_hdr = NULL;
  189. char* datapart = NULL;
  190. icmp_hdr = (IcmpHeader*)icmp_data;
  191. //ICMP报文类型设置为回显请求
  192. icmp_hdr->i_type = ICMP_ECHO;
  193. icmp_hdr->i_cksum = 0;
  194. //获取当前经常IP作为标识符
  195. icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
  196. icmp_hdr->i_cksum = 0;
  197. icmp_hdr->i_seq = 0;
  198. datapart = icmp_data + sizeof(IcmpHeader);
  199. //以数字0天吃剩余空间
  200. memset(datapart, 0, datapart - sizeof(IcmpHeader));
  201. }
  202. //释放资源函数
  203. void FreeRes() 
  204. {
  205. //关闭创建的套字节
  206. if (m_socket!=INVALID_SOCKET)
  207. {
  208. closesocket(m_socket);
  209. }
  210. //释放分配的内存
  211. HeapFree(GetProcessHeap(), 0, recvbuf);
  212. HeapFree(GetProcessHeap(), 0, icmp_data);
  213. //注销WSAStartup()调用
  214. WSACleanup();
  215. return;
  216. }
  217. //解读Ip选项头函数
  218. void DecoideIPptions(char *buf,int bytes) 
  219. {
  220. IpOptionHeader* ipopt = NULL;
  221. IN_ADDR inaddr;
  222. int i;
  223. HOSTENT* host = NULL;
  224. //获取路由信息的入口地址
  225. ipopt = (IpOptionHeader*)(buf + 20);
  226. printf(" RR:    ");
  227. for (i = 0; i < (ipopt->ptr / 4) - 1; i++)
  228. {
  229. inaddr.S_un.S_addr = ipopt->addr[i];
  230. if (i!=0)
  231. {
  232. printf(" ");
  233. //根据IP地址获取主机名
  234. host = gethostbyaddr((char*)& inaddr.S_un.S_addr, sizeof(inaddr.S_un.S_addr), AF_INET);
  235. }
  236. //如果获取到了主机名,则输出主机名
  237. if (host)
  238. {
  239. printf("(%-15s) %s\n",inet_ntoa(inaddr),host->h_name);
  240. }
  241. else
  242. {
  243. //否则输出ip地址
  244. printf("(%-15s)\n", inet_ntoa(inaddr));
  245. }
  246. return;
  247. }
  248. }
  249. //解读ICMP报头函数
  250. void DecodeICMPHeader(char* buf,int bytes,SOCKADDR_IN* from) 
  251. {
  252. IpHeader* iphdr = NULL;
  253. IcmpHeader* icmphdr = NULL;
  254. unsigned short iphdrlen;
  255. DWORD tick;
  256. static int icmpcount = 0;
  257. iphdr = (IpHeader*)buf;
  258. //计算ip报头的长度
  259. iphdrlen = iphdr->h_len * 4;
  260. tick = GetTickCount();
  261. //如果ip报头的长度为最大长度(基本长度是20字节),则认为有ip选项,因此需要解读ip选项
  262. if ((iphdrlen==MAX_IP_HDR_SIZE)&&(!icmpcount))
  263. {
  264. //解读IP选项,即路由信息
  265. DecoideIPptions(buf,bytes);
  266. }
  267. //如果收到的不是回显应答的报文则报错
  268. if (icmphdr->i_id != ICMP_ECHOREPLY)
  269. {
  270. printf("nonecho type %d recvd \n",icmphdr->i_type);
  271. return;
  272. }
  273. //核实用收到的ID号和发送的是否一致
  274. if (icmphdr->i_id != (USHORT)GetCurrentProcessId())
  275. {
  276. printf("someone else's packet! \n");
  277. return;
  278. }
  279. //输出信息记录信息
  280. printf("%d bytes from %s:",bytes,inet_ntoa(from->sin_addr));
  281. printf(" icmp_seq =%d. ", icmphdr->i_seq);
  282. printf(" icmp_seq =%d. ", tick - icmphdr->timestamp);
  283. printf("\n");
  284. icmpcount++;
  285. return;
  286. }
  287. //ping 函数
  288. void PingTest(int timeout) 
  289. {
  290. int ret;
  291. int readNum;
  292. int fromlen;
  293. struct hostenthp = NULL;
  294. //创建原始套接字,该套接字用于ICMP
  295. m_socket = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED);
  296. //如果套接字创建不成功
  297. if (m_socket==INVALID_SOCKET)
  298. {
  299. printf("WSASocket() Failed: %d \n",WSAGetLastError());
  300. return;
  301. }
  302. //要求记录路由选项
  303. if (RecordFlag)
  304. {
  305. //IP选项每个字段都初始化为零
  306. ZeroMemory(&IpOption, sizeof(IpOption));
  307. //为每一个ICMP包设置路由选项
  308. IpOption.code = IP_RECORD_ROUTE;
  309. IpOption.ptr = 4;
  310. IpOption.len = 39;
  311. ret = setsockopt(m_socket, IPPROTO_IP, IP_OPTIONS, (char*)& IpOption, sizeof(IpOption));
  312. if (ret==SOCKET_ERROR)
  313. {
  314. printf("setsockopt(IP_OPTIONS) failed:%d \n",WSAGetLastError());
  315. }
  316. }
  317. //设置接收的超时值
  318. readNum = setsockopt(m_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)& timeout, sizeof(timeout));
  319. if (readNum==SOCKET_ERROR)
  320. {
  321. printf("setsockopt(SO_RCVTIMEO) failed:%d\n ", WSAGetLastError());
  322. return;
  323. }
  324. //初始化目的的地址为零
  325. memset(&DestAddr, 0sizeof(DestAddr));
  326. //设置地址族,这里表示使用IP地址族
  327. DestAddr.sin_family = AF_INET;
  328. if ((DestAddr.sin_addr.S_un.S_addr= inet_addr(lpdest)) == INADDR_NONE)
  329. {
  330. //名字解析,根据主机名获取IP地址
  331. if ((hp=gethostbyname(lpdest))!=NULL)
  332. {
  333. //将获取到的IP地址赋值给目的地址中相对应的字段
  334. memcpy(&(DestAddr.sin_addr), hp->h_addr_list, hp->h_length);
  335. //将获取到的地址族群赋值到目的相对应的字段中
  336. DestAddr.sin_family = hp->h_addrtype;
  337. printf("DestAddr.sin_addr = %s \n",WSAGetLastError());
  338. }
  339. //获取不成功
  340. else
  341. {
  342. printf("gethostbyname() faild: %d \n ",WSAGetLastError());
  343. return;
  344. }
  345. }
  346. //数据报文需要包含ICMP报头
  347. datasize += sizeof(IcmpHeader);
  348. //根据默认堆句柄,从堆中分配MAX_PACKET内存块,新分配内存将初始化为零
  349. icmp_data = (char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);
  350. recvbuf = (char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,MAX_PACKET);
  351. //如果奉陪内存不成功
  352. if (!icmp_data)
  353. {
  354. printf("HeapAlloc() failed: %d \n", GetLastError());
  355. return;
  356. }
  357. //创建ICMP报文
  358. memset(icmp_data, 0, MAX_PACKET);
  359. FillICMPData(icmp_data, datasize);
  360. while (1)
  361. {
  362. static int nCount = 0;
  363. int writeNum;
  364. //超过指定的记录条数则退出
  365. if (nCount++ == PacketNum)
  366. {
  367. break;
  368. }
  369. //计算校验和前要把校验和字节设置为零
  370. ((IcmpHeader*)icmp_data)->i_cksum = 0;
  371. //获取操作系统启动到现在所经过的毫秒数,设置时间搓
  372. ((IcmpHeader*)icmp_data)->timestamp= GetTickCount();
  373. //设置序列号
  374. ((IcmpHeader*)icmp_data)->i_seq = seq_no++;
  375. //计算校验和
  376. ((IcmpHeader*)icmp_data)->i_cksum= CheckSum((USHORT*)icmp_data,datasize);
  377. //开始发送ICMP请求
  378. writeNum = sendto(m_socket,icmp_data,datasize,0,(struct sockaddr*)&DestAddr,&fromlen);
  379. //如果接收不成功
  380. if (readNum==SOCKET_ERROR)
  381. {
  382. //如果超时则不成功
  383. if (WSAGetLastError()==WSAETIMEDOUT)
  384. {
  385. printf("timed out \n");
  386. continue;
  387. }
  388. //其他发送不成功的原因
  389. printf("sendto() failed:%d \n",WSAGetLastError());
  390. return;
  391. }
  392. //开始接收ICMP应答
  393. fromlen = sizeof(SourceAddr);
  394. readNum = recvfrom(m_socket,recvbuf,MAX_PACKET,0,(struct sockaddr*)&SourceAddr,&fromlen);
  395. if (readNum == SOCKET_ERROR)
  396. {
  397. //如果超时则不成功
  398. if (WSAGetLastError() == WSAETIMEDOUT)
  399. {
  400. printf("timed out \n");
  401. continue;
  402. }
  403. //其他发送不成功的原因
  404. printf("recvfrom() failed:%d \n", WSAGetLastError());
  405. return;
  406. }
  407. //解读收到的ICMP数据报
  408. }
  409. }
  410. int main(int argc,char* argv[])
  411. {
  412. InitPing();
  413. GetArgments(argc,argv);
  414. PingTest(1000);
  415. //延迟1s
  416. Sleep(1000);
  417. if (SucessFlag)
  418. {
  419. printf("\n Ping end,you have got %.0f records! \n",PacketNum);
  420. }
  421. else
  422. {
  423. printf("Ping end ,no record");
  424. }
  425. FreeRes();
  426. getchar();
  427. return 0;
  428. /*
  429. */
  430. }



欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739

评价
这一世以无限游戏为使命!
排名
2
文章
634
粉丝
44
评论
93
docker中Sware集群与service
尘叶心繁 : 想学呀!我教你呀
一个bug让程序员走上法庭 索赔金额达400亿日元
叼着奶瓶逛酒吧 : 所以说做程序员也要懂点法律知识
.net core 塑形资源
剑轩 : 收藏收藏
映射AutoMapper
剑轩 : 好是好,这个对效率影响大不大哇,效率高不高
ASP.NET Core 服务注册生命周期
剑轩 : http://www.tnblog.net/aojiancc2/article/details/167
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术