【网络编程2】网络编程基础-发送ICMP包(Ping程序)
IP协议
网络地址和主机协议
位于网络层的协议,主要目的是使得网络能够互相通信,该协议使用逻辑地址跨网络通信,目前有两个版本IPV4,IPV6。
在IPV4协议中IP地址是一个32位的数备,采用点分四组的表示法便于使用。每个IP地址包含两个部分,网络地址和主机地址。
网络地址和主机地址的划分由子网掩码来决定。网络地址用来标示所连接到的局域网,主机地址则标示设备本身,子网掩码与IP地址等长,被设为1的部分标示IP地址的对应部分为网络地址,设为0则标示IP对应位为主机地址。
IP格式
0-3 版本:IP所使用的版本
4-7 首部长度:IP头的长度(4字节的整数倍)
8-15 服务类型:路由器使用该字段做流量优先排序
16-18 | 19-31 总长度:IP长度+数据包长度
32 标示符:一个唯一标示,用来区分数据包或数据分片的顺序
32 标记:标示数据包是否分片数据
32 分片偏移:分片数据时该字段有效,用于将数据包按顺序组合
64 存活时间:定义数据包的生存周期,路由器中转一次该值减一
64 协议:用来识别数据包上层协议类型
64 首部校验和:错误检测机制,确认内容是否损坏或被篡改
96 源IP地址:发送方主机的IP地址
128 目的IP地址:接收方主机的IP地址
160 选项:可选
160 or 192+ 数据:使用IP传递的实际数据
ICMP协议
IP协议并不是一个可靠的协议,它不保证数据被送达,那么保证数据送达的工作应该由其他的模块来完成。其中一个重要的模块就是ICMP(网络控制报文)协议。
ICMP(internet control message protocol)是internet控制报文协议。是TCP/IP协议族里的一个子协议,用于在IP主机、路由器之间传递控制信息。
控制信息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。
ICMP协议大致分为两类,一种是查询报文,一种是差错报文。查询报文由发送者发出,差错报文是由出错的主机返回发给源数据包的发送者。
PING可以说是ICMP的最著名的应用,系统自带工具当某一个网站上不去的时候。通常会ping一下这个网站,ping会回显一些有用的信息。
ICMP协议的格式
ICMP头相对较小并根据用途而改变,ICMP报文的前4个字节是统一的格式,共有三个字段:即类型,代码和检验和。
ICMP报文类型
各种类型的ICMP报文如所示,不现类型由报文中的类型字段和代码字段来共同决定。
常见的类型和代码有
ICMP回显(echo)请求和辉县应答报文格式
ICMP地址掩码请求和应答
ICMP时间戳请求与应答
ICMP不可达报文、ICMP超时报文
ICMP重定向报文
ICMP路由器请求报文格式
ICMP路由器通告报文格式
ICMP源站抑制差错报文格式
等等。。。。。
ICMP地址掩码请求和应答
ICMP时间戳请求与应答
编程操作
1、定义结构体
因为ICMP是基于IP协议运行的,所以我们要先定义IP格式的结构体;
以下的是三个结构体分别对应了IP、ICMP请求、ICMP应答的发包格式。。
定义IP首部格式
//定义IP首部格式
typedef struct _IPHeader
{
u_char vile; // 版本和首部长度
u_char ser; // 服务类型
u_short totalLen; // 总长度
u_short id; // 标示符
u_short flag; // 标记+分片偏移
u_char ttl; // 存活时间
u_char protocol; // 协议
u_short checkSum; // 首部校验和
in_addr srcIP; // 源IP地址
in_addr destIP; // 目的IP地址
}IPHeader, *PIPHDR;
定义ICMP头部格式
//ICMP头部
typedef struct _ICMPHeader
{
u_char type; // 类型
u_char code; // 代码
u_short checkSum; // 校验和
u_short id; // 标示符
u_short seq; // 序列号
}ICMPHeader, *PICMPHDR;
ICMP时间戳请求报文格式
//ICMP时间戳请求报文
typedef struct _ECHOREQUEST
{
ICMPHeader icmpHeader; //ICMP头部
int time; //记录ping时间
char data[32]; //数据
}ECHOREQUEST, *pechorequest;
ICMP时间戳应答报文格式
//ICMP时间戳应答报文
typedef struct _ECHORESPONSE
{
IPHeader ipHeader;
ECHOREQUEST echoRequest;
char fill[255]; //接收其他多余的应答数据
}ECHORESPONSE, *PECHORESPONSE;
2、ICMP发送和接收
发送ICMP数据包函数
将请求的包的宽度写入结构体中,构造出ICMP的请求格式向目标服务器发送数据。
//发送ICMP数据包 sendEchoReQuest
void sendEchoReQuest(SOCKET sock, sockaddr_in dstIP)
{
static int id = 1;
static int seq = 1;
//ICMP请求
ECHOREQUEST echoRequest = { 0 };
//主要是用来记录请求应答的时间,当发送ECHO请求时记录发送时间,当接受到应答数据时,在用GetTickcount()- echoRequest.time这样就能得到请求应答需要多少时间了。
echoRequest.time = GetTickCount();
echoRequest.icmpHeader.type = 8;
echoRequest.icmpHeader.code = 0;
echoRequest.icmpHeader.id = id++;
echoRequest.icmpHeader.seq = seq++;
echoRequest.icmpHeader.checkSum =
checksum((u_short*)&echoRequest, sizeof(echoRequest));
int re = sendto(sock,
(char *)&echoRequest,
sizeof(echoRequest),
0,
(sockaddr*)&dstIP,
sizeof(dstIP));
if (re == SOCKET_ERROR)
{
printf(" send error \n ");
}
return;
}
接收ICMP数据包函数
有对应的发送数据包动作,对方服务器如果存活那么会发回来响应包;
//接收ICMP数据包 recvEchoReQuest
void recvEchoReQuest(SOCKET sock,
ECHORESPONSE * sponse, sockaddr_in *dstIP)
{
int size = sizeof(sockaddr);
int re = recvfrom(sock, (char *)sponse, sizeof(ECHORESPONSE), 0,
(sockaddr*)dstIP, &size);
if (re == SOCKET_ERROR)
{
printf(" recvfrom error");
}
return;
}
附录A:如何计算检验和
// 计算校验和(16位二进制反码求和)
u_short checksum(u_short *buffer, int len)
{
register int nleft = len;
register u_short *w = buffer;
register u_short answer;
register int sum = 0;
// 使用32bit的累加器,进行16bit的反馈计算
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
// 补全奇数位
if (nleft == 1) {
u_short u = 0;
*(u_char *)(&u) = *(u_char *)w;
sum += u;
}
// 将反馈的16bit从高位移至地位
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
sum += (sum >> 16); /* add carry */
answer = ~sum; /* truncate to 16 bits */
return (answer);
}
ICMP中检验和的计算算法为:
1、将检验和字段置为0
2、把需校验的数据看成以16位为单位的数字组成,依次进行二进制反码求和
3、把得到的结果存入检验和字段中
所谓二进制反码求和,就是:
1、将源数据转成反码
2、0+0=0 0+1=1 1+1=0进1
3、若最高位相加后产生进位,则最后得到的结果要加1
在实际实现的过程中,比较常见的代码写法是:
1、将检验和字段置为0
2、把需校验的数据看成以16位为单位的数字组成,依次进行求和,并存到32位的整型中
3、把求和结果中的高16位(进位)加到低16位上,如果还有进位,重复第3步[实际上,这一步最多会执行2次]
4、将这个32位的整型按位取反,并强制转换为16位整型(截断)后返回
附录B VS2015 UAC设置
使用原始套接字编程调试的时候运行权限不够,是无法运行的。
可以在VS里做设置,在需要提升权限的时候自动重启VS提升权限。
点击项目右键->属性->配置属性->链接器->清单文件->UAC执行级别->requireAdministrator(选择)。
实例代码
将上面的代码与思路整合就成了下面的实例代码;
#include "stdafx.h"
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include "data.h"
//发送ICMP数据包 sendEchoReQuest
void sendEchoReQuest(SOCKET sock, sockaddr_in dstIP)
{
static int id = 1;
static int seq = 1;
//ICMP请求
ECHOREQUEST echoRequest = { 0 };
//主要是用来记录请求应答的时间,当发送ECHO请求时记录发送时间,当接受到应答数据时,在用GetTickcount()- echoRequest.time这样就能得到请求应答需要多少时间了。
echoRequest.time = GetTickCount();
echoRequest.icmpHeader.type = 8;
echoRequest.icmpHeader.code = 0;
echoRequest.icmpHeader.id = id++;
echoRequest.icmpHeader.seq = seq++;
echoRequest.icmpHeader.checkSum =
checksum((u_short*)&echoRequest, sizeof(echoRequest));
int re = sendto(sock,
(char *)&echoRequest,
sizeof(echoRequest),
0,
(sockaddr*)&dstIP,
sizeof(dstIP));
if (re == SOCKET_ERROR)
{
printf(" send error \n ");
}
return;
}
//接收ICMP数据包 recvEchoReQuest
void recvEchoReQuest(SOCKET sock,
ECHORESPONSE * sponse, sockaddr_in *dstIP)
{
int size = sizeof(sockaddr);
int re = recvfrom(sock, (char *)sponse, sizeof(ECHORESPONSE), 0,
(sockaddr*)dstIP, &size);
if (re == SOCKET_ERROR)
{
printf(" recvfrom error");
}
return;
}
//ping函数
void Ping(char * host)
{
//2.创建套接字
SOCKET sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
//获取域名对应的IP
HOSTENT * lpHost = gethostbyname(host);
sockaddr_in dstIP = { 0 };
dstIP.sin_family = AF_INET;
dstIP.sin_addr.S_un.S_addr = *(u_long*)lpHost->h_addr;
for (int i=0;i<4;i++)
{
//发送ICMP数据
sendEchoReQuest(sock, dstIP);
//接收ICMP数据
ECHORESPONSE sponse;
recvEchoReQuest(sock, &sponse, &dstIP);
printf("来自 %s 的回复: 字节=32 时间=%dms TTL=%d \n",
inet_ntoa(dstIP.sin_addr),
GetTickCount() - sponse.echoRequest.time,
sponse.ipHeader.ttl
);
}
}
int main()
{
//1.初始化winsock环境
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2),&wsa);
while (true)
{
Ping("www.baidu.com");
char in = getchar();
if (in == 'q')
{
break;
}
}
//2.释放环境
WSACleanup();
return 0;
}
参考:
C++实现Ping
http://www.cnblogs.com/goagent/p/4078940.html
给VS程序添加管理员权限等
http://www.voidcn.com/blog/wokaowokaowokao12345/article/p-5973906.html
【网络编程2】网络编程基础-发送ICMP包(Ping程序)的更多相关文章
- 当发送ICMP包的时候不一定能收得到(arp已经应答了)【复现不了了】
arp已经应答了,然后再返回ICMP应答的时候竟然不被回复. 其实这里想想也很容易想清楚: 虽然arp给了回复,但是真正到ICMP报文到的时候,我理解报文到的时候,我理解还是要进行与本地网络兑换的,本 ...
- Python网络编程之网络基础
Python网络编程之网络基础 目录 Python网络编程之网络基础 1. 计算机网络发展 1.1. OSI七层模型 1.2. 七层模型传输数据过程 2. TCP/IP协议栈 2.1 TCP/IP和O ...
- Linux高性能server编程——Linux网络基础API及应用
Linux网络编程基础API 具体介绍了socket地址意义极其API,在介绍数据读写API部分引入一个有关带外数据发送和接收的程序,最后还介绍了其它一些辅助API. socket地址API 主 ...
- C#网络编程(一)基础篇
简介: C#网络编程API包含在System.Net和System.Net.Sockets命名空间下,大部分网络操作都可以在其中找到相应的类来实现:包括Socket的创建和连接,网络流收发方法的封装, ...
- (53)LINUX应用编程和网络编程之八Linux网络基础
3.8.1.网络通信概述 3.8.1.1.从进程间通信说起:网络域套接字socket,网络通信其实就是位于网络中不同主机上面的2个进程之间的通信. 3.8.1.2.网络通信的层次 (1)硬件部分:网卡 ...
- 老师的blog整理 .网络编程部分 .网络编程部分 前端部分 django基础部分
老师的blog整理 .网络编程部分 .网络编程部分 前端部分 django基础部分 老师的blog整理 python基础部分: 宝哥blog: https://www.cnblogs.com/gu ...
- 网络协议 10 - Socket 编程(上):实践是检验真理的唯一标准
系列文章传送门: 网络协议 1 - 概述 网络协议 2 - IP 是怎么来,又是怎么没的? 网络协议 3 - 从物理层到 MAC 层 网络协议 4 - 交换机与 VLAN:办公室太复杂,我要回学校 网 ...
- 黑马程序员:Java编程_网络编程
=========== ASP.Net+Android+IOS开发..Net培训.期待与您交流!=========== 网络编程就是两个(或多个)设备(例如计算机)之间的数据传输,更具体的说,网络编程 ...
- 物联网网络编程、Web编程综述
本文是基于嵌入式物联网研发工程师的视觉对网络编程和web编程进行阐述.对于专注J2EE后端服务开发的童鞋们来说,这篇文章可能稍显简单.但是网络编程和web编程对于绝大部分嵌入式物联网工程师来说是一块真 ...
随机推荐
- MT【67】窥一斑知全豹
已知$f(x)=ax^2+bx+c$在$x\in\{-1,0,1\}$时满足$|f(x)|\le1$ 求证:当$|x|\le1$时$|f(x)|\le\frac{5}{4}$. 证明: $$f(x)= ...
- MT【49】四次函数求最值
已知$f(x)=(1-x^2)(x^2+ax+b)$的图像关于x=3对称,求$f(x)$的最大值. 解答:显然$-1,7;1,5$是$f(x)=0$的根.故$(x^2+ax+b)=(x-5)(x-7) ...
- 自学Aruba7.3-Aruba安全认证-802.1x认证(web页面配置)
点击返回:自学Aruba之路 自学Aruba7.3-Aruba安全认证-802.1x认证(web页面配置) 步骤1 建立AP Group,命名为test802-group 步骤2 将AP加入到AP ...
- [luogu1447][bzoj2005][NOI2010]能量采集
题目大意 求出\(\sum_{i=1}^{n} \sum_{i=1}^{m} gcd(i,j)\times 2 -1\). 题解 解法还是非常的巧妙的,我们考虑容斥原理.我们定义\(f[i]\)表示\ ...
- python 线程间事件通知
这是线程间最简单的通信机制:一个线程发送事件,其他线程等待事件事件机制使用一个内部的标志,使用set方法进行使能为True,使用clear清除为falsewait方法将会阻塞当前线程知道标记为True ...
- 【LOJ#10180】烽火传递 单调队列+dp
题目大意:给定一个 N 个非负整数数组成的序列,每个点有一个贡献值,现选出其中若干数,使得每连续的 K 个数中至少有一个数被选,要求选出的数贡献值最小. 题解:设 \(dp[i]\) 表示考虑了序列前 ...
- 初探typescript
学习任何知识都不是一蹴而就的.typescript也是如此.今天我们来初步的了解一下typescript的安装与编译.以及基础的语法知识. 第一步:安装ts 第二步:在安装好的文件夹里面,写上ts文件 ...
- mac java jdk 安装删除
安装: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 下载安装双击安装 卸载: ...
- python字典遍历的几种方法
(1)遍历key值 >>> a {'} >>> for key in a: print(key+':'+a[key]) a:1 b:2 c:3 >> ...
- 过时date.toLocaleString()的解决方法
System.out.println(new java.util.Date()); 输出:Thu Jan 27 14:43:28 CST 2011 System.out.println(new jav ...