正在ping www.baidu.com的程序运行实例
源代码片段
抓包实例
源代码
#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
//套接字宏错误
#define sockerror(str) cout << #str <<"错误代码:" << WSAGetLastError() << endl;
using namespace std;
//链接WinSock2的动态库
#pragma comment(lib,"ws2_32.lib")
//将字符型数组IP地址导入int 参数:地址数组指针
unsigned int char_int_ip(unsigned char *str)
{
unsigned int sum = 0; //准备好容器
for (int c = 0; c < 4; c++) // 一个IP地址就四位够用了
{
sum += *str++; //让容器加上地址数组第一个字节
if (c != 3) //等于三的时候代表最后一位不用再位移
{
sum <<= 8; //加到的结果都右移8位,刚好一个字节,重复三次再加上最后一个char字节完成
}
}
return sum; //返回结果
}
//ICMP报文格式结构体
struct ICMP_DATA
{
unsigned char type; //类型描述占8位
unsigned char code; //代码描述占8位
unsigned short chec; //检验和描述占16位
unsigned short id; //用来表示此请求的唯一ID号,通常设置为进程id
unsigned short seq; //序列号
}ICMP;
//ICMP头部检验和
u_short head(struct ICMP_DATA* picmp, int len)
{
long sum = 0;
unsigned short* pusicmp = (unsigned short*)picmp;
while (len > 1)
{
sum += *(pusicmp++);
if (sum & 0x80000000)
sum = (sum & 0xffff) + (sum >> 16);
len -= 2;
}
if (len)
sum += (unsigned short)*(unsigned char*)pusicmp;
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return (unsigned short)~sum;
}
int main(int argc, char** argv)
{
//键盘敲烂 工资过万
cout << "开发者:2849551294@QQ.COM" << endl;
//先判断用户是否输入了参数
if (argc < 2 )
{
cout << "请键入参数!" << endl;
return false;
}
//定义所需要的变量
SOCKADDR_IN desaddr; //定义套接字地址结构体变量,目的地址
SOCKADDR_IN fromaddr; //接收到的地址
char fromadd[16] = { 0 }; //为接收回复得到的地址准备
char fromstr[72] = { 0 }; //为接收到的数据准备
unsigned char* dnsaddr; //为指向地址解析准备的无符号字符型指针
addrinfo dnsaddinfo, *saveaddr; //为保存地址解析到的地址信息,第一个用来指定解析地址的类型,第二个保存地址数据
unsigned int sec = 1, sockopt = 1000; //第一个为序列号准备,第二个为超时时间准备 1000等于1秒
unsigned char* dadd = new unsigned char[4]; //为保存地址解析的结果准备的4位动态内存
//开始构造ICMP头部
char data[64 + 8] = { 0 }; //数组,存放要发送的64位数据与8位ICMP头部
ICMP_DATA* icmp = (ICMP_DATA*)data; //将数组强转为ICMP头部类型 初始化ICMP头部 不然会报使用未初始化内存的错误
icmp->type = 8; //ICMP头部类型字段
icmp->code = 0; //ICMP头部代码字段
icmp->chec = 0; //ICMP头部检验和字段置零
icmp->id = 6; //ICMP头部id字段
icmp->seq = 1; //ICMP头部序列号字段
memcpy((data + 8), "zaima?zaima?zaima?zaima?zaima?zaima?zaima?zaima?zaima?zaima????", 64);//64位数据+8位头部数据
icmp->chec = head((ICMP_DATA*)data, sizeof(data)); //计算检验和并赋值给头部检验和字段,错一点都会导致失败
//初始化套接字
WSADATA wsastat;//传出参数 结构体类型对象
if (0 != WSAStartup(0X202, &wsastat)) //参数1 主版本号 副版本号 参数2 一个结构体
{ //MAKEWORD将2,2分别放到short的两个字节中
sockerror(初始化套接字失败!)//获取套接字错误代码
system("pause");
return false;
}
//解析地址
memset(&dnsaddinfo, NULL, sizeof(dnsaddinfo));//内存初始化函数
dnsaddinfo.ai_family = AF_INET; //使用IPv4地址协议
dnsaddinfo.ai_socktype = SOCK_STREAM; //套接字类型TCP
if (0 != getaddrinfo(argv[1], NULL, &dnsaddinfo, &saveaddr)) //解析argv[1]字符串,使用addinfo指定的类型去解析,保存到addr,成功返回0
{
sockerror(地址解析失败...)
return SOCKET_ERROR;
}
//输出地址
dnsaddr = (unsigned char*)saveaddr->ai_addr; //用上面准备的无符号字符型指针指向解析到的地址
dnsaddr += 4; //不知道为什么解析到的地址前面多了2.0.0.0四位数,让指向的地址加4略过
cout << "正在请求";
for (int i = 0; i < 4; i++) //将解析到的地址打印
{
printf("%d.", *dnsaddr++); //打印地址
}
dnsaddr -= 4; //再让地址加4回到地址首部,后续还需要使用
printf("\b 的应答:\n");
delete[]dadd; //释放掉创建的动态内存,地址解析已完结
//开始创建套接字,并设置相关参数
SOCKET sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP); //创建原始套接字ICMP协议
if (sock == INVALID_SOCKET)
{
sockerror(创建套接字失败!)//获取套接字错误代码
system("pause");
return false; //失败返回false
}
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&sockopt, sizeof(sockopt));//设置接收超时,1000毫秒收不到就跳过接收
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&sockopt, sizeof(sockopt));//设置发送超时,1000毫秒发送不成功就跳过
desaddr.sin_family = AF_INET; //设置目标地址协议IPv4
desaddr.sin_addr.S_un.S_addr = htonl(char_int_ip(dnsaddr));//设置目标地址,htonl将地址转为网络字节序
desaddr.sin_port = htons(0);//端口0 htons主机字节序
while (true) //循环的发送接收
{
sendto(sock, data, sizeof(data), NULL, (sockaddr*)&desaddr, sizeof(desaddr));//发送 1.套件字 2.要发送的数据包含ICMP头 3.一般不使用 4.目的地址结构体 5.目的地址结构体大小
ULONGLONG time, ptime = GetTickCount64(); //从这里获取时间 给ptime ,time存时间差
int in = sizeof(fromaddr); //获取接收地址的长度给到in,为formrecv准备。因为类型原因只能这样
recvfrom(sock, fromstr, sizeof(fromstr), NULL, (SOCKADDR*)&fromaddr, &in);接收 1.套件字 2.存储接收到的数据包含ICMP头 3.一般不使用 4.保存着接收到的地址 5.地址结构体大小
unsigned char tll = fromstr[8]; //接收到的数据里第9为是tll值
time = GetTickCount64() - ptime; //再获取一次时间,用这次时间减去上次的时间,得到接收花了多久
char* ptr = (char*)inet_ntop(AF_INET, &fromaddr.sin_addr, fromadd, sizeof(fromadd)); //将接收到的二进制地址转换成点分十进制地址 ,1.IPv4协议 2.接收到的二进制地址 3.转换完成后保存到的数组 4.数组大小
char fuhao = 0;
if (time >= 1000) //如果接收的时间超过设置的超时时间,就是超时
{
cout << "请求超时..." << endl;
continue; //超时了就退出这一次操作,从新发送接收
}
if (time == 0) //如果时间等于0毫秒就让它符合变成 <
{
time = 1; //太快了time会是0的,太难看让它等于1再输出
fuhao = '<';
}
else //如果不是小于1的话就是等于咯
{
fuhao = '=';
}
if (fromstr[20] == 0 || fromstr[21] == 0)//类型与代码字段都等于0的话就是正常的回复消息
{ //其实还有很多错误处理比如主机不可达等等,作者懒 也不检查数据包完整性了
cout << "来自-> " << fromadd << " 的回复 " << "字节=64 " << "时间"
<< fuhao << time << "ms" << " TLL= " << dec << (int)tll << " 序列号->" << sec++ << endl;
}
Sleep(500); //设置半秒,不然太快看不清
}
return false;
}