广工ping程序c++实现

1)程序预处理
导入库文件、加载头文件和定义常量。

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#pragma comment(lib, “ws2_32.lib”)
#define ICMP_ECHOREPLY 0 //ICMP回应答复
#define ICMP_ECHOREQ 8 //ICMP回应请求
#define REQ_DATASIZE 32 //请求数据报大小
2)源代码:
Ping.h:
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#pragma comment(lib, “ws2_32.lib”)
#define ICMP_ECHOREPLY 0 //ICMP回应答复
#define ICMP_ECHOREQ 8 //ICMP回应请求
#define REQ_DATASIZE 32 //请求数据报大小
class Ping{
private:
/*IP 报头,标准IPV4占20字节
*/
typedef struct _IPHeader
{
	u_char VIHL; //4位版本号和4位首部长度
	u_char ToS; //8 位服务类型
	u_short TotalLen; //16 位总长度
	u_short ID; //16 位标识符: 作用是分片后的重组
	u_short Frag_Flags;//3 位标志加 13 位片偏移:总16位
	u_char TTL; //8 位生存时间
	u_char Protocol; //8 位上层协议号: 指出是何种协议
	u_short Checksum; //16 位校验和
	struct in_addr SrcIP; //32 位源 IP 地址
	struct in_addr DestIP; //32 位目的 IP 地址
}IPHDR;

/*ICMP 报头,一共八个字节,前四个字节为:类型(1字节)、
   代码(1字节和检验和(2字节)。后四个字节取决于类型*/
	typedef struct _ICMPHeader
{
	u_char Type; //8 位类型字段
	u_char Code; //8 位代码字段
	u_short Checksum; //16 位校验和
	u_short ID; //16 位标识符
	u_short Seq; //16 位序列号
	char Data; //数据
}icmpHd;


typedef struct _ECHOREQUEST  	//定义ICMP回应请求
{
	icmpHd icmpHd;//imcp首部
	DWORD dwTime;//请求数据报的发送时间
	char cData[REQ_DATASIZE];//请求数据报的数据部分大小
}ECHOREQUEST;

typedef struct _ECHOREPLY     //定义ICMP回应答复
{
	IPHDR ipHdr;//ip首部
	ECHOREQUEST echoRequest;//icmp回应
	char cFiller[256];       //数据部分
}ECHOREPLY;


public:

	Ping();
	u_short checksum(u_short *buffer, int len);
	int pack(SOCKET s, struct sockaddr_in *lpstToAddr);
	DWORD unpack(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL);
	int Waiting(SOCKET s);
	void Pingtest(char pstrHost, const char param);};

Ping.cpp:

#define _CRT_SECURE_NO_DEPRECATE
#include “ping.h”
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#pragma comment(lib, “ws2_32.lib”)

Ping::Ping() {};
u_short Ping::checksum(u_short *buffer, int len)//校验和计算方法
{
	register u_short answer;
	register int sum = 0;
	/使用32位累加器,进行16位的反馈计算/
	while (len > 1)
	{
		sum += *buffer++;
		len -= 2;
	}
	/补全奇数位/
	if (len == 1)
	{
		u_short u = 0;
		*(u_char *)(&u) = (u_char)buffer;
		sum += u;
	}
	/将反馈的16位从高位移到低位/
	sum = (sum >> 16) + (sum & 0xffff);
	sum += (sum >> 16);
	answer = ~sum;
	return (answer);
	}

int Ping::pack(SOCKET s, struct sockaddr_in IPToAddr) //封装ICMP报文并发送回应请求方法
{
	static ECHOREQUEST echoReq; //实例化为请求icmp报文
	static int Id = 1; //设置初始标识id
	static int Seq = 1; //设置初始序列号
	int n;
	/为ICMP报文初始化/
	echoReq.icmpHd.Type = ICMP_ECHOREQ; //设置为请求类型
	echoReq.icmpHd.Code = 0; //请求代码为0
	echoReq.icmpHd.Checksum = 0; //初始化为0
	echoReq.icmpHd.ID = Id++; //标识自增
	echoReq.icmpHd.Seq = Seq++; //序列自增
	for (n = 0; n < REQ_DATASIZE; n++)//简单填充一下要发送的ICMP报文的数据部分
	{
		echoReq.cData[n] = ‘1’ + n;
	}
	//存储发送时,操作系统启动所经过时间。
	/GetTickCount()函数:GetTickCount返回(retrieve)从操作系统启动所经过(elapsed)的毫秒数,它的返回值是DWORD。/
	echoReq.dwTime = GetTickCount();
		echoReq.icmpHd.Checksum = checksum((u_short)&echoReq, sizeof(ECHOREQUEST)); //计算回应请求的校验和
	/发送回应请求/
	n = sendto(s, (LPSTR)&echoReq, sizeof(ECHOREQUEST), //sendto() 用来将数据传给对方主机.
	0, (struct sockaddr*)IPToAddr, sizeof(SOCKADDR_IN)); //和参数flags 一般设0,参数to 用来指定欲传送的网络地址, 参数tolen 为sockaddr 的结果长度.
	if (n == SOCKET_ERROR) //失败返回-1, 错误原因存于errno 中.
	{
		printf(“send to() 方法发生错误,错误原因:%d\n”, WSAGetLastError()); //将错误原因显示出来
	}
	return (n); //成功则返回实际传送出去的字符数,
}


DWORD Ping::unpack(SOCKET s, LPSOCKADDR_IN IPFromAddr, u_char *pTTL)//接收并进行解析报文
{
	ECHOREPLY echoReply; //定义一个icmp回复
	int n;
	int Len = sizeof(struct sockaddr_in);
	/接收应答回复/
	n = recvfrom(s, (LPSTR)&echoReply, sizeof(ECHOREPLY), 0, (LPSOCKADDR)IPFromAddr, &Len);
	if (n == SOCKET_ERROR) //检验接收结果
	{
		printf(“recvfrom() 方法发生错误,错误原因:%d\n”, WSAGetLastError());
	}
	*pTTL = echoReply.ipHdr.TTL; //记录返回的TTL
	return(echoReply.echoRequest.dwTime); //返回应答时间
}

/等待回应答复,使用select模型进行监听是否在读取数据/
int Ping::Waiting(SOCKET s)
{
	struct timeval timeout;
	fd_set readfds;
	readfds.fd_count = 1;
	readfds.fd_array[0] = s;
	timeout.tv_sec = 1;
	timeout.tv_usec = 0;
	return(select(1, &readfds, NULL, NULL, &timeout));//监听是否可以从这些文件中读取数据了。
	//select函数返回值:负值:select错误 正值:某些文件可读写或出错 0:等待超时,没有可读写或错误的文件
}

/PING功能的主要实现步骤/
void Ping::Pingtest(char *IputHost)
{
	SOCKET rawSocket; //定义原始套接字
	LPHOSTENT lpHost; //用于存放主机信息的hosten结构:
	struct sockaddr_in destIP;
	struct sockaddr_in srcIP;
	DWORD dwTimeSent;
	DWORD time;
	u_char cTTL;
	int Pingtimes;
	int n, Min = 100000, Max = 0, average = 0;
	int sent = 4, reveived = 0, lost = 0;

	/*创建原始套接字,ICMP类型*/
	rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);//ipv4
	if (rawSocket == SOCKET_ERROR)
	{
		printf("socket() 方法发生错误,错误原因:%d\n", WSAGetLastError());
		return;
	}

	/*检测目标主机*/
	lpHost = gethostbyname(IputHost); //获取域名或者32位的网络字节ip地址
	 //gethostbyname()返回对应于给定主机名的包含主机名字和地址信息的hostent结构的指针
	if (lpHost == NULL)
	{
		printf("找不到目标主机,请确认目标地址输入是否正确:%s\n", IputHost);
		return;
	}

	/*设置目标机地址*/
	destIP.sin_addr.s_addr = *((u_long FAR*)(lpHost->h_addr)); //设置目标IP的32位
	destIP.sin_family = AF_INET;//设置为IPv4
	destIP.sin_port = 0;//假设端口为0
	/*提示开始进行PING*/
	printf("\n");
	printf("以下是Pingtest所得到的信息:");
	printf("\n正在Ping %s [%s] 具有 %d 字节的数据\n", IputHost, 	inet_ntoa(destIP.sin_addr), REQ_DATASIZE);
	/*需要显示出点分十进制的IP地址,需要用到inet_ntoa进行转换*/

	int i = 0;
	if (!strcmp(param, “-t”))
	{
		i = 1;
	}
	/发起PING测试/
	for (Pingtimes = 0; Pingtimes < 4||i==1; Pingtimes++) {
	pack(rawSocket, &destIP); //封装和发送ICMP回应请求
	/等待回复的数据/
	n = Waiting(rawSocket);
	if (n == SOCKET_ERROR)
	{
	printf(“select() 方法发生错误,错误原因:%d\n”, WSAGetLastError());
	break;
	}
	if (!n)
	{
		lost++;
		printf("\nREQUEST TimeOut.");
		continue;//如果超时,跳过后面接收解析过程
	}
	/* 接收回复*/
	dwTimeSent = unpack(rawSocket, &srcIP, &cTTL);
	reveived++;
	time = GetTickCount() - dwTimeSent; //计算花费的时间
	if (time > Max)Max = time; //求最大值
	if (time < Min)Min = time; //求最小值
	average += time;
	printf("\n REPLY FROM %s: bytes = %d time = %ldms TTL = %d",
	inet_ntoa(srcIP.sin_addr), REQ_DATASIZE, time, cTTL);
	}
	printf("\n\n");
	printf("%s 的 Ping 统计信息:\n", inet_ntoa(srcIP.sin_addr));
	printf(" 数据包: 发送 = %d, 收到 = %d, 丢失 = %d (%.f%% loss),\n",
	sent, reveived, lost, (float)(lost*1.0 / sent) * 100);
	if (lost == 0)
	{
		printf(“往返行程的估计时间(以毫秒为单位):\n”);
		printf(" 发送时间:最小值 = %dms, 最大值 = %dms, 平均值 = %dms\n", Min, Max, average / sent);
	}
	printf("\n\n");
	n = closesocket(rawSocket);//关闭连接
	if (n == SOCKET_ERROR)//当连接不能正常关闭时,返回null
	{
		printf(“closesocket() error:%d\n”, WSAGetLastError());//将
	}
}

Main.cpp:

#define _CRT_SECURE_NO_DEPRECATE
#include “ping.h”
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
using namespace std;
#pragma comment(lib, “ws2_32.lib”)
char* isParamEmpty(char *buffer, char *param);

int main()
{
	char dstAddr[20];
	char noparam[] = “no_param”;
	char *param = NULL;
	printf("\n");
	printf(“请输入你要进行Ping测试的地址\n”);

	/*使用gethostname()函数,必须先进行下面的初始化操作*/
	WSADATA wsd;//检测输入的参数
	//初始化Winsock
	if (WSAStartup(MAKEWORD(1, 1), &wsd) != 0)
	{
		printf("加载 Winsock 失败!\n");
	}
	while (1) {
		cout << "ping ";
		dstAddr[0] = '\0';
		cin.getline(dstAddr, 20);
		if ((param = isParamEmpty(dstAddr, param)) == NULL) param = noparam;
		Ping ping = Ping();
		ping.Pingtest(dstAddr, param);
		cin.clear();//用来更改cin的状态标示符的
		cin.sync();//清除缓存区的数据流的
	}
	WSACleanup();
	return 0;

}
char *isParamEmpty(char *buffer, char *param)//判断-t是否输入
{
	char *temp = NULL;
	temp = buffer;
	while (*temp !=\0)
	{
		if (*temp == ’ ')
		{
			*temp =\0;
			param = ++temp;
		}
		temp++;
	}
	return param;
}

————————————————
版权声明:本文为CSDN博主「Cmy_894」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Cmy_894/article/details/107292670

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值