目的
通过C++编程实现ping, 也算是对于SOCKET编程初步了解掌握。
- 了解ICMP协议
- 了解对应socket编程
关于ICMP
请参考我的博客Ping工作原理
socket 编程实现ICMP
Ping 使用 Internet 控制消息协议(ICMP)来测试主机之间的连通性。当用客户端发送一个 ping 请求时,则对应的发送一个 ICMP Echo 请求消息到目标主机,并等待目标主机回复一个 ICMP Echo 回应消息。如果目标主机接收到请求并且网络连接正常,则会返回一个回应消息,表示主机之间的网络连接是正常的。如果目标主机没有收到请求消息或网络连接不正常,则不会有回应消息返回。
编译error
-
winsock2.h 与 windows.h 先后顺序导致编译错误
在Windows环境下编程不可避免的会用到windows.h和winsock.h头文件,在默认情况下windows.h头文件会包含winsock.h,此时当尝试包含winsock.h时就会出现头文件定义冲突的情况。解决这个冲突的方式有两种,第一种,在头部定义#define WIN32_LEAN_AND_MEAN来主动去除winsock.h头文件包含。第二种是将#include <winsock2.h>头文件,放在#include<windows.h>之前。两种方式均可,这些方法在进行Windows套接字编程时非常重要,可以防止头文件冲突,确保编译顺利进行。 -
IPExport.h导致编译错误
还有一种是我遇到的 OptionsData 未知重写说明符, Data 未知重写说明符
需要将如下注释掉就行, 具体原因后续有时间整理下。
//#include <iphlpapi.h>
//#include <icmpapi.h>
代码
- ICMPDataHeader.h
相关头文件声明 与 数据定义
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <iostream>
#include <vector>
#include <cmath>
#include <chrono>
#include <ctime>
//#include <iphlpapi.h>
//#include <icmpapi.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib, "ws2_32.lib")
//#pragma comment(lib, "iphlpapi.lib")
#define IP_RECORD_ROUTE 0x7
// IP header structure
typedef struct _iphdr
{
//Suppose the BYTE_ORDER is LITTLE_ENDIAN
unsigned int h_len:4; // Length of the header
unsigned int version:4; // Version of IP
unsigned char tos; // Type of service
unsigned short total_len; // Total length of the packet
unsigned short id; // Unique identification
unsigned short frag_offset; // Fragment offset
unsigned char ttl; // Time to live
unsigned char protocol; // Protocol (TCP, UDP etc)
unsigned short checksum; // IP checksum
unsigned int sourceIP; // Source IP
unsigned int destIP; // Destination IP
} IpHeader;
#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0
#define ICMP_MIN 8 // Minimum 8-byte ICMP packet (header)
// ICMP header structure
// This is not the standard header, but we reserve space for time
typedef struct _icmphdr
{
BYTE i_type;
BYTE i_code; // Type sub code
USHORT i_cksum;
USHORT i_id;
USHORT i_seq;
ULONG timestamp;
} IcmpHeader;
// IP option header - use with socket option IP_OPTIONS
typedef struct _ipoptionhdr
{
unsigned char code; // Option type
unsigned char len; // Length of option hdr
unsigned char ptr; // Offset into options
unsigned long addr[9]; // List of IP addrs
} IpOptionHeader;
#define DEF_PACKET_SIZE 32 // Default packet size
#define MAX_PACKET 1024 // Max ICMP packet size
#define MAX_IP_HDR_SIZE 60 // Max IP header size w/options
- 关键核心代码 cpp
校验函数
#include "icmppacketprocessor.h"
ICMPPacketProcessor::ICMPPacketProcessor(QObject* parent)
:QObject{
parent}
{
}
ICMPPacketProcessor::~ICMPPacketProcessor()
{
if(lpdest){
delete lpdest;
}
}
unsigned short ICMPPacketProcessor::checksum(unsigned short *buffer, int size)
{
unsigned long cksum=0;
while(size > 1){
cksum += *buffer++;
size -= sizeof(unsigned short);
}
if(size)
{
cksum += *(UCHAR*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (unsigned short)(~cksum);
}
// Function: FillICMPData
// Description:
// Helper function to fill in various fields for our ICMP request
void ICMPPacketProcessor::fillICMPData(char *icmp_data, int datasize)
{
IcmpHeader *icmp_hdr = NULL;
char *datapart = NULL;
icmp_hdr = (IcmpHeader*)icmp_data;
icmp_hdr->i_type = ICMP_ECHO; // Request an ICMP echo, type is 8
icmp_hdr->i_code = 0; // code is 0
icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
icmp_hdr->i_cksum = 0;
icmp_hdr->i_seq = 0;
datapart = icmp_data + sizeof(IcmpHeader);
// Place some junk in the buffer
memset(datapart, 'E' , datasize - sizeof(IcmpHeader));
}
// Function: DecodeIPOptions
// Description:
// If the IP option header is present, find the IP options
// within the IP header and print the record route option values
void ICMPPacketProcessor::decodeIPOptions(char *buf, int bytes)
{
IpOptionHeader *ipopt = NULL;
IN_ADDR inaddr;
int i;
HOSTENT *host = NULL;
ipopt = (IpOptionHeader *)(buf + 20);
printf("Record Route: ");
for(i = 0; i < (ipopt->ptr / 4) - 1; i++)
{
inaddr.S_un.S_addr = ipopt->addr[i];
if (i != 0) printf(" ");
host = gethostbyaddr((char *)&inaddr.S_un.S_addr, sizeof(inaddr.S_un