TCP/IP实验,实验三,执行ping操作

#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;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值