#pragma warning(disable:4996)
#include "stdafx.h"
#include <string>
#include <WinSock2.h>
#include <iostream>
#include <iphlpapi.h>
#include <WS2tcpip.h>
#pragma comment(lib,"iphlpapi.lib")
#pragma comment(lib,"WS2_32.lib")
#define DEF_PACKET_SIZE 32
#define MAX_PACKET 64
#define ICMP_ECHO 8
#define INVALLD_SOCKET 0
#define ICMP_MIN 8
#define ICMP_ECHO_REPLY 0
typedef struct iphdr {
unsigned int h_len : 4;
unsigned int version : 4;
unsigned char tos;
unsigned short total_len;
unsigned short ident;
unsigned short frag_and_flags;
unsigned char ttl;
unsigned char proto;
unsigned short checksum;
unsigned int sourceIP;
unsigned int destIP;
}IpHeader;
typedef struct _ihdr
{
BYTE i_type;
BYTE i_code;
USHORT i_cksum;
USHORT i_id;
USHORT i_seq;
ULONG timestamp;
}IcmpHeader;
USHORT checksum(USHORT* pBuf, int iSize) {
unsigned long cksum = 0;
while (iSize > 1)
{
cksum += *pBuf++;
iSize -= sizeof(USHORT);
}
if (iSize)
{
cksum += *(USHORT*)pBuf;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return(USHORT)(~cksum);
}
void fill_icmp_data(char* icmp_data, int datasize)
{
IcmpHeader* icmp_hdr;
char* datapart;
//将缓冲区转换为icmp——data结构
icmp_hdr = (IcmpHeader*)icmp_data;
//填充各字段的值
icmp_hdr->i_type = ICMP_ECHO;//将类型设置为ICMP响应包
icmp_hdr->i_code = 0;//编码为0
icmp_hdr->i_id = (USHORT)GetCurrentThreadId();//编号设置为当前线程的编号
icmp_hdr->i_cksum = 0;//校验和为0
icmp_hdr->i_seq = 0;//序列号为0
datapart = icmp_data + sizeof(IcmpHeader);//定义到数据部分
//在数据部分随便填充一些数据
memset(datapart, 'E', datasize - sizeof(IcmpHeader));
}
int decode_resp(char* buf, int bytes, struct sockaddr_in* from, DWORD tid)
{
IpHeader* iphdr;//IP数据包头
IcmpHeader* icmphdr;//ICMP包头
unsigned short iphdrlen;//包头长度
iphdr = (IpHeader*)buf;//从buf中获取ip数据包头的指针
//计算包头长度
iphdrlen = iphdr->h_len * 4;//number of 32-bit words*4=bytes
//指定缓冲区长度小于ip包头加上最小的icmp包长度
//包不完整或者不包含ICMP数据
if (bytes < iphdrlen + ICMP_MIN) {
return -1;
}
//定位到icmp包头起始位置
icmphdr = (IcmpHeader*)(buf + iphdrlen);
//不是回应包则不处理
if (icmphdr->i_type != ICMP_ECHO_REPLY) {
return -2;
}
//发送的ICMP包id与接收到的ICMP包id对应
if (icmphdr->i_id != (USHORT)tid) {
return -3;
}
//返回发送ICMP包和接收回应包的时间差
int time = GetTickCount() - (icmphdr->timestamp);
if (time >= 0)
return time;
else
return -4;
}
int ping(const char* ip, DWORD timeout)
{
WSADATA wsaData;//初始化socket数据
SOCKET sockRaw = NULL;//执行ping操作的socket
struct sockaddr_in dest, from; //socket通信的地址
struct hostent* hp;//保存主机信息
int datasize;//发送数据包大小
char *dest_ip;//目的地址
char* icmp_data = NULL;//icmp包的数据
char* recvbuf = NULL;//保存应答数据
USHORT SEQ_NO = 0;
int ret = -1;
int bread;
unsigned int addr;
int bwrote;
int fromlen;
int time;
DWORD startTime;
//初始化socket
if (WSAStartup(MAKEWORD(2, 1), &wsaData) != 0)
{
ret = -1000;
goto FIN;//wsastartup错误
}
//创建原始Socket
sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
//出错转到最后
if (sockRaw == INVALLD_SOCKET) {
ret = -2;
goto FIN;//wsasocket错误
}
//socket接收超时选项
bread = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
if (bread == SOCKET_ERROR)
{
ret = -3;
goto FIN;
}
//socket发送超时选项
bread=setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
if (bread == SOCKET_ERROR)
{
ret = -4;
goto FIN;
}
memset(&dest, 0, sizeof(dest));
addr = 0;//ip地址转换为网络字节序
hp = gethostbyname(ip);//获得远程主机名称
if (!hp)
{
addr = inet_addr(ip);
}
if ((!hp) && (addr == INADDR_NONE))
{
ret = -5;
goto FIN;
}
//配置远程通信地址
if (hp != NULL)
memcpy(&(dest.sin_addr), hp->h_addr, hp->h_length);
else
dest.sin_addr.s_addr = addr;
if (hp)
dest.sin_family = hp->h_addrtype;
else
dest.sin_family = AF_INET;
dest_ip = inet_ntoa(dest.sin_addr);
//准备要发送的数据
datasize = DEF_PACKET_SIZE + sizeof(IcmpHeader);
char icmp_dataStack[MAX_PACKET];
char recvbufStack[MAX_PACKET];
icmp_data = icmp_dataStack;
recvbuf = recvbufStack;
//未分配到足够的空间
if (!icmp_data)
{
ret = -6;
goto FIN;
}
memset(icmp_data, 0, MAX_PACKET);
//准备要发送的数据
fill_icmp_data(icmp_data, datasize);
((IcmpHeader*)icmp_data)->i_cksum=0;
startTime = GetTickCount();
((IcmpHeader*)icmp_data)->timestamp = startTime;
((IcmpHeader*)icmp_data)->i_seq = SEQ_NO++;
((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);
//发送数据
bwrote = sendto(sockRaw, icmp_data, datasize, 0, (struct sockaddr*)&dest, sizeof(dest));
if (bwrote == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAETIMEDOUT)
{
ret = -7;
goto FIN;
}
}
if (bwrote < datasize) {
ret = -8;
goto FIN;
}
//使用QueryPerformance函数精确判断结果返回时间值
//原有的返回与应用程序相差太大
LARGE_INTEGER ticksPerSecond;
LARGE_INTEGER start_tick;
LARGE_INTEGER end_tick;
double elapsed; //经过的时间
QueryPerformanceFrequency(&ticksPerSecond);//cpu每秒几个ticks
QueryPerformanceCounter(&start_tick);//计数器初始位置
fromlen = sizeof(from);//源地址大小
while (1)
{
//接收回应包
bread = recvfrom(sockRaw, recvbuf, MAX_PACKET, 0, (struct sockaddr*)&from, &fromlen);
if (bread == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
ret = -1;//超时
goto FIN;
}
ret = -9;
goto FIN; //接收错误
}
//对回应的ip数据包进行解析,定位ICMP数据
time = decode_resp(recvbuf, bread, &from, GetCurrentThreadId());
if (time >= 0)
{
//ret=time;
QueryPerformanceCounter(&end_tick);//获取结束时系统计数器的值
double m = (double)(end_tick.QuadPart - start_tick.QuadPart);
elapsed = (m /(double)ticksPerSecond.QuadPart);//计算ping操作的用时
ret = (int)(elapsed * 1000);
goto FIN;
}
else if (GetTickCount() - startTime >= timeout || GetTickCount() < startTime)
{
ret = -1;//超时
goto FIN;
}
}
FIN:
//释放资源
closesocket(sockRaw);
WSACleanup();
return ret;//返回错误编号
}
int _tmain(int argc, _TCHAR* argv[])
{
if (argc != 2)
{
printf("参数数量不正确。请指定要ping的IP地址。\n");
return -1;
}
printf("ping %s...\n", argv[1]);
int ret = ping(argv[1], 500);
if (ret >= 0)
{
printf("%s在线,执行ping操作用时%dms。\n", argv[1], ret);
}
else
{
switch (ret)
{
case -1:
printf("ping超时。\n");
break;
case -2:
printf("创建socket出错。\n");
break;
case -3:
printf("设置socket的接收超时选项出错。\n");
break;
case -4:
printf("设置socket的发送超时选项\n");
break;
case -5:
printf("获取域名时出错,可能是IP地址不正确。\n");
break;
case -6:
printf("未能为ICMP数据包分配到足够的空间\n");
break;
case -7:
printf("发送ICMP数据包出错\n");
break;
case -8:
printf("发送ICMP数据包的数量不正确\n");
break;
case -9:
printf("接收ICMP数据包出错\n");
break;
case -1000:
printf("初始化Windows Sockets环境出错\n");
break;
default:
printf("未知的错误\n");
break;
}
}
printf("\n");
system("pause");
return 0;
}
TCP/IP实验,实验三,执行ping操作
最新推荐文章于 2024-09-05 09:59:35 发布