计算机网络做了一个附加题,用C语言Raw_Socket实现ping指令。

通过本部的Mooc学习了一下Socket编程,然后成功写了出来orz

先放一下代码:

  1. #include <stdio.h>
  2. #include <winsock2.h>
  3. #include <time.h>
  4. #include <iostream>
  5. #include <windows.h>
  6. #include <Mmsystem.h>
  7. #pragma comment(lib,"winmm.lib")
  8. #pragma comment(lib,"ws2_32.lib")
  9.  
  10. using namespace std;
  11. const int IP_Header_min_lenght=20;
  12. /*
  13. Ver 4
  14. Hlen 4
  15. 1
  16. Servicetype 8
  17. 1
  18. Total length 16
  19. 2
  20. Identifier 16
  21. 2
  22. flag and frag offset 16
  23. 2
  24. TimeToLive 8
  25. 1
  26. Prorocol 8
  27. CHECKSUM 16
  28. SRC ADDR 32
  29. DES ADDR 32
  30. 160/8=20
  31. */
  32. typedef struct icmp_hdr
  33. {
  34. unsigned char icmp_type; // 类型
  35. unsigned char icmp_code; // 代码
  36. unsigned short icmp_checksum; // 校验和
  37. unsigned short icmp_id; // ID号,设为PID
  38. unsigned short icmp_sequence; // 序列号
  39. unsigned long icmp_timestamp; // 时间戳
  40. } ICMP_HDR, *PICMP_HDR;
  41.  
  42. unsigned short checksum(USHORT* buff, int size)
  43. {
  44. unsigned long cksum = 0;
  45. while(size>1)
  46. {
  47. cksum += *buff++;
  48. size -= sizeof(USHORT);
  49. }
  50. // 是奇数
  51. if(size)
  52. {
  53. cksum += *(UCHAR*)buff;
  54. }
  55. // 将32位的chsum高16位和低16位相加,然后取反
  56. cksum = (cksum >> 16) + (cksum & 0xffff);
  57. cksum += (cksum >> 16);
  58. return (USHORT)(~cksum);
  59. }
  60.  
  61. bool SetTimeout(SOCKET sRaw, int nTime)
  62. {
  63. int ret = setsockopt(sRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTime, sizeof(nTime));
  64. return ret != SOCKET_ERROR;
  65. }
  66.  
  67. double GetTickCountA()
  68. {
  69. __int64 Freq = 0;
  70. __int64 Count = 0;
  71. if(QueryPerformanceFrequency((LARGE_INTEGER*)&Freq)&& Freq > 0&& QueryPerformanceCounter((LARGE_INTEGER*)&Count))
  72. {
  73. //乘以1000,把秒化为毫秒
  74. return (double)Count / (double)Freq * 1000.0;
  75. }
  76. return 0.0;
  77. }
  78. int main()
  79. {
  80.  
  81. char website_name[]="www.google.com";// www.baidu.com www.google.com
  82. char *DestIp;
  83. double succ=0;
  84. double fail=0;
  85. int min_RTT=999999999;
  86. int max_RTT=-1;
  87. int sum_RTT=0;
  88. DestIp = (char*)malloc(20*sizeof(char));
  89. //初始化WindowsSocketsAPI
  90. WSADATA wsaData;
  91. WORD sockVersion = MAKEWORD(2,2);//声明版本 2.2
  92. WSAStartup(sockVersion, &wsaData);//启动
  93. //获取IP地址
  94. struct hostent *curr_hostent = gethostbyname(website_name);
  95. DestIp = inet_ntoa( *(struct in_addr*)curr_hostent->h_addr_list[0] );
  96. //DestIp = "192.168.88.1";
  97. printf("正在ping %s:[%s] \n",website_name,DestIp);
  98. //printf("%s\n",curr_hostent->h_addr_list[0]);
  99. //printf("%s\n",inet_ntoa(*(struct in_addr*)curr_hostent->h_addr_list[0]));
  100. if(!curr_hostent){
  101. puts("Get IP address error!");
  102. //system("pause");
  103. // exit(0);
  104. }
  105. //创立套接字
  106. SOCKET sRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);//AF_INET=ipv4 SOCK_RAW ICMP协议
  107. SetTimeout(sRaw, 1000);
  108. // 设置目的地址
  109. SOCKADDR_IN dest;
  110. dest.sin_family = AF_INET;//ipv4地址家族
  111. dest.sin_port = htons(0);//将整型变量从主机字节顺序转变成网络字节顺序
  112. dest.sin_addr.S_un.S_addr = inet_addr(DestIp);
  113. // 创建ICMP数据包并填写
  114. char buff[sizeof(ICMP_HDR) + 32];
  115. ICMP_HDR* pIcmp = (ICMP_HDR*)buff;
  116. pIcmp->icmp_type = 8; // 请求一个ICMP回显
  117. pIcmp->icmp_code = 0;
  118. pIcmp->icmp_id = (unsigned short)GetCurrentProcessId();
  119. unsigned short nSeq = 0;
  120. char recvBuf[1024];
  121. SOCKADDR_IN from;
  122. int nLen = sizeof(from);
  123. for(int o=0;o<10;o++)
  124. {
  125. int nRet;
  126. pIcmp->icmp_checksum = 0;//为了后续checksum函数正常 需要先置位0
  127. pIcmp->icmp_timestamp = GetTickCountA();
  128. pIcmp->icmp_sequence = nSeq++;
  129. pIcmp->icmp_checksum = checksum((unsigned short*)buff, sizeof(ICMP_HDR) + 32);
  130. //发送包
  131. nRet = sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR *)&dest, sizeof(dest));//发送
  132. if(nRet == SOCKET_ERROR)
  133. {
  134. printf(" sendto() failed: %d \n", ::WSAGetLastError());
  135. return -1;
  136. }
  137. //接收包
  138. nRet = recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen);
  139. if(nRet == SOCKET_ERROR)
  140. {
  141. if(WSAGetLastError() == WSAETIMEDOUT)
  142. {
  143. printf(" Request timed out\n");
  144. fail++;
  145. continue;
  146. }
  147. fail++;
  148. printf(" Failed. Error code: %d\n", WSAGetLastError());
  149. return -1;
  150. }
  151. // 解析包
  152. int nTick = GetTickCountA();
  153. if(nRet < IP_Header_min_lenght + sizeof(ICMP_HDR))
  154. {
  155. printf(" Returned too few bytes from %s \n", inet_ntoa(from.sin_addr));
  156. fail++;
  157. }
  158. // 接收包中包含IP头,所以加20得到ICMP头
  159. ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + IP_Header_min_lenght); // (ICMP_HDR*)(recvBuf + sizeof(IPHeader));
  160. if(pRecvIcmp->icmp_type != 0) // 回显
  161. {
  162. if(pRecvIcmp->icmp_type == 3) // 回显
  163. {
  164. printf(" Unreachable\n");fail++;
  165. }
  166. else if(pRecvIcmp->icmp_type == 4) // 回显
  167. {
  168. printf(" Origin suppression \n");fail++;
  169. }
  170. else if(pRecvIcmp->icmp_type == 5) // 回显
  171. {
  172. printf(" Redirect \n");fail++;
  173. }
  174. else if(pRecvIcmp->icmp_type == 8) // 回显
  175. {
  176. printf(" Echo request\n");fail++;
  177. }
  178. else if(pRecvIcmp->icmp_type == 9) // 回显
  179. {
  180. printf(" Router announcement\n");fail++;
  181. }
  182. else if(pRecvIcmp->icmp_type == 10) // 回显
  183. {
  184. printf(" Router request \n");fail++;
  185. }
  186. else if(pRecvIcmp->icmp_type == 11) // 回显
  187. {
  188. printf(" Time out \n");fail++;
  189. }
  190. else if(pRecvIcmp->icmp_type == 17) // 回显
  191. {
  192. printf(" Address subnet request\n");fail++;
  193. }
  194. else if(pRecvIcmp->icmp_type == 18) // 回显
  195. {
  196. printf(" Address subnet response\n");fail++;
  197. }
  198. return -1;
  199. }
  200. succ++;
  201. printf(" 来自 %s的回复: ", inet_ntoa(from.sin_addr));
  202. printf(" 字节=%d bytes :", nRet, inet_ntoa(from.sin_addr));
  203. printf(" 时间= %d ms", nTick - pRecvIcmp->icmp_timestamp);
  204. printf(" TTL= %d \n", *(recvBuf+8));
  205. min_RTT=min(min_RTT,(int)(nTick - pRecvIcmp->icmp_timestamp));
  206. max_RTT=max(max_RTT,(int)(nTick - pRecvIcmp->icmp_timestamp));
  207. sum_RTT=sum_RTT+nTick - pRecvIcmp->icmp_timestamp;
  208. Sleep(1000);//一秒
  209. }
  210. printf("数据包丢失率:%lf",fail*100/(succ+fail));
  211. cout<< "%"<<endl;
  212. printf("最小 RTT = %d ms \n", min_RTT);
  213. printf("最大 RTT = %d ms\n", max_RTT);
  214. printf("平均 RTT = %d ms\n", sum_RTT/10);
  215.  
  216. return 0;
  217. }

还是算比较基础的吧,注释里面该有的都有啦~

主要就是socket编程和IP数据报的知识。

对于socket编程:我在编写raw_socket抓包程序时去听了本部计算机网络的MOOC,其中一章就是关于socket编程的。其中讲了声明winsocket版本、建立套接字、发送与接受等等等等函数。在编写程序时我们找到对应的函数调用即可。这里我有一个之前误解的地方:我原以为是gethostbyname将站点名翻译为32位点分地址,但在实际编写程序并调试后才发现是inet_ntoa的功劳。在了解了调用socket函数创立套接字、用gethostbyname和inet_ntoa获得ip地址、用sendto和recvfrom收发包后,接下来的工作就是填写数据包了。

我们需要写的是一个IP数据报,这与在创建socket时填写的AF_INET参数相一致。他在IP数据报的首部后的数据部分即为ICMP报文,中间有六个项,在接下来的核心代码注解中详细展示。我们需要创建一个ICMP报文大小+IC数据报头大小的缓冲区,填写每一段的内容后发送。这里有一个细节时为了正确计算校验和,需要先将其置零并填写完其余所有数据后再计算。在接受到返回的数据后,用与抓包程序类似的解析方式即可。

再放一些查资料找到的东西备用吧:

sin_zero用于填充结构体使之保持与struct sockaddr一样的长度。

(从onenote复制下来是张图片...

感谢本部mooc拯救我于苦海之中

计算机网络 Raw_Socket编程 Ping C语言的更多相关文章

  1. winsock教程- windows下的socket编程(c语言实现)

    winsock教程- windows下的socket编程(c语言实现) 使用winsock进行socket 编程     这是一个学习windows下socket编程(c语言)的快速指南.这是因为一下 ...

  2. 计算机网络 & 网络编程 期末总结与测评题

      第一部分:网络编程部分的相关知识 Socket套接字定义: 套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开.读写和关闭等操作.套接字允许应用程序 ...

  3. 我为什么反对推荐新人编程C/C++语言入门?

    虽然我接触编程以及计算机时间比较早,但是正式打算转入程序员这个行当差不多是大学第四年的事情 从03年接触计算机,07年开始接触计算机编程, 期间接触过的技术包括 缓冲区溢出(看高手写的shellcod ...

  4. 结对编程项目——C语言实现WordCount Web化

    结对编程项目 代码地址 201631062219,201631011410 gitee项目地址:https://gitee.com/xxlznb/pair_programming 作业地址:https ...

  5. extern的使用详解(多文件编程)——C语言

    extern——关键字 extern是C语言中的一个关键字,一般用在变量名前或函数名前,作用是用来说明“此变量/函数是在别处定义的,要在此处引用”,extern这个关键字大部分读者应该是在变量的存储类 ...

  6. 编程基础-c语言中指针、sizeof用法总结

    1.指针 学习 C 语言的指针既简单又有趣.通过指针,可以简化一些 C 编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的.所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的. ...

  7. 第一章 T-SQL查询和编程基础 T-SQL语言基础(1)

    T-SQL查询和编程基础(1) 1.1 理论背景 SQL是为查询和管理关系型数据库管理系统(RDBMS)中的数据而专门设计的一种标准语言. RDBMS是一种基于关系模型的数据库管理系统,关系模型则是一 ...

  8. 【自学编程】C语言编程简单的小程序,计算长方体体积!

    计算长方体体积 有朋友会说长方体体积还不好算吗?长X宽X高.没错用计算器一下就可以出结果,编程反而麻烦些,但是我们说的是这种思维,如果复杂的重复运算的话写好程序就非常简单了. 简单运算下一个固定高度的 ...

  9. 【嵌入式】C语言高级编程▁▁▁嵌入式C语言入门编程学习!

    ✍  1.C 语言标准 什么是 C 语言标准呢? 我们生活的现实世界,就是由各种标准构成的,正是这些标准,我们的社会才会有条不紊的运行. 比如我们过马路,遵循的交通规则就是一个标准:红灯停,绿灯行,黄 ...

随机推荐

  1. .NET6运行时动态更新限流阈值

    昨天博客园撑不住流量又崩溃了,很巧正在编写这篇文章,于是产生一个假想:如果博客园用上我这个限流组件会怎么样呢? 用户会收到几个429错误,并且多刷新几次就看到了内容,不会出现完全不可用. 还可以降低查 ...

  2. Spring Cloud Gateway实战之四:内置predicate小结

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  3. [atARC119D]Grid Repainting 3

    将每一行和每一列分别作为一个点,当第$i$行第$j$列的格子为红色时,将第$i$行与第$j$列连边 此时,考虑选择第$i$行的红色格子并将第$i$行的格子全部改成白色: 关于这一操作的条件,即需要第$ ...

  4. [atARC110E]Shorten ABC

    考虑令$a$.$b$和$c$分别对应1.2和3,那么每一次相当于令$x$和$y$变为$x\oplus y$(要求$x\ne y$) 根据异或的结合律,我们相当于将其划分为若干个区间求异或值 (另外还有 ...

  5. [spojRNG]Random Number Generator

    先将所有数加上Ri,即变为区间[0,2Ri],考虑容斥,将区间容斥为[0,+oo)-[2Ri,+oo),然后对[2Ri,+oo)令$bi=ai-2Ri$,相当于范围都是[0,+oo)问题转化为求n个正 ...

  6. git连接远程仓库

    1. 连接远程仓库 1.1. 创建仓库 在连接远程仓库之前,得先要确定你有一个远程仓库,到GitHub官网搞一个账户. 点右上角的加号然后"New repository"输入一个仓 ...

  7. Vue自定义组件实现v-model指令

    Tips: 本文所描述的Vue均默认是Vue2版本 在我们初次接触Vue的时候,一定会了解到一个语法糖,那就是v-model指令,它带给我们的第一印象就是它可以实现双向绑定 那么,什么是双向绑定?通俗 ...

  8. DirectX12 3D 游戏开发与实战第十二章内容

    12.几何着色器 如果不启用曲面细分,那么几何着色器这个可选阶段将会在位于顶点着色器和像素着色器之间.顶点着色器以顶点作为输入数据,而几何着色器以完整的图元为输入数据.与顶点着色器不同的是,顶点着色器 ...

  9. [R] read.table/read.delim读入数据行数变少?

    以为对read.table/read.delim很熟了,谁知又掉坑里了. 我有个3万多行的数据集,包括样品表达量和注释信息.大概长这样: 本来3万多行,可是读进来的时候变成了1万多行,而且read.d ...

  10. BSA分析

    目录 两种算法 1. 欧氏距离(ED)算法 2. SNP-index算法 实操 1. 上游分析 2. 下游分析 两种算法 1. 欧氏距离(ED)算法 mut与wt分别代表突变型混池.野生型混池,A.C ...