您的位置:首页 > 编程语言 > C语言/C++

Ping程序C++实现

2012-05-15 17:31 381 查看
学习内容,参见《Windows网络编程》第13章原始套接字

Ping程序实现步骤

创建类型为SOCK_RAW的一个套接字,同时设定协议IPPROTO_ICMP。
创建并初始化ICMP头。
调用sendto或WSASendto,将ICMP请求发给远程主机。
调用recvfrom或WSARecvfrom,以接收任何ICMP响应。

ICMP简介

ICMP是(Internet ControlMessage Protocol)Internet控制报文协议。用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。

系统自带ping程序,检测百度是否可访问。



自己实现ping程序,检测百度是否可访问。

要显示访问IP地址,该IP即为系统自带ping程序解析IP,如上图为119.75.218.77。



使用windows的Winsock 2编程,需要进行工程配置。

工程右键Properties->ConfigurationProperties->Linker->Input->Additional Dependencies中添加ws2_32.lib。
Demo代码采用Multi-Byte方式,设置Properties->ConfigurationProperties->General->Character Set为Use Multi-Byte Character Set。

备注:所有关系到收发数据的缓冲都属于简单的char类型,这些函数没有Unicode版本。当字符集为Unicode时,需要进行字符串转换。

程序源码,MyPing.cpp。

// MyPing.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <winsock2.h>
#include <WS2tcpip.h>
#include <stdio.h>
#include <stdlib.h>

#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0
#define ICMP_MIN 8 //Minimum 8-byte ICMP packet (header)

#define DEF_PACKET_SIZE 32
#define MAX_PACKET 1024
#define MAX_IP_HDR_SIZE 60

//IP header structure
typedef struct _iphdr
{
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 ident;//Unique identifier
unsigned short frag_and_flags;//Flags
unsigned char ttl;//Time to live
unsigned char proto;//Protocol (TCP,UDP,etc.)
unsigned short checksum;//IP checksum

unsigned int sourceIP;
unsigned int destIP;
} IpHeader;

//ICMP header structure
typedef struct _icmphdr
{
BYTE i_type;
BYTE i_code;//Type sub code
USHORT i_cksum;
USHORT i_id;
USHORT i_seq;

//This is not the standard header, but we reserve space for time
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 optons
unsigned long addr[9];//List of IP addrs
} IpOptionHeader;

int datasize;
char* lpdest;

//Print usage information
void usage()
{
printf("usage:MyPing -i:IP [data size]\n");
printf("    -i:IP           remote machine to Ping\n");
printf("    datasize     can be up to 1 KB\n");

ExitProcess(1);
}

//Helper function to fill in various fields for our ICMP request
void FillICMPData(char* icmp_data, int datasize)
{
IcmpHeader* icmp_hdr = (IcmpHeader*)icmp_data;
icmp_hdr->i_type = ICMP_ECHO;//Request an ICMP echo
icmp_hdr->i_code = 0;
icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
icmp_hdr->i_cksum = 0;
icmp_hdr->i_seq = 0;

char* datapart = icmp_data + sizeof(IcmpHeader);

//Place some junk in the buffer
memset(datapart, 'E', datasize - sizeof(IcmpHeader));
}

//This function calculates the 16-bit one's complement sum
//of the supplied buffer (ICMP) header
USHORT checksum(USHORT* buffer, int size)
{
unsigned long cksum = 0;

while (size > 1)
{
cksum += *buffer++;
size -= sizeof(USHORT);
}

if (size)
{
cksum += *(UCHAR*)buffer;
}

cksum = (cksum>>16) + (cksum & 0xffff);
cksum += (cksum>>16);

return (USHORT)(~cksum);
}

//If the IP option header is present, find the IP options
//within the IP header and print the record route option values
void DecodeIPOptions(char* buf, int bytes)
{
IpOptionHeader* ipopt = (IpOptionHeader*)(buf + 20);

printf("RR:    ");
for (int i = 0; i < (ipopt->ptr / 4) - 1; i++)
{
IN_ADDR inaddr;
inaddr.S_un.S_addr = ipopt->addr[i];

if (i != 0)
{
printf("        ");
}

HOSTENT* host = gethostbyaddr((char*)&inaddr.S_un.S_addr,
sizeof(inaddr.S_un.S_addr), AF_INET);
if (host)
{
printf("(%-15s) %s\n", inet_ntoa(inaddr), host->h_name);
}
else
{
printf("(%-15s)\n", inet_ntoa(inaddr));
}
}

return;
}

//The response is an IP packet. We must decode the IP header to
//locate the ICMP data.
void DecodeICMPHeader(char* buf, int bytes, struct sockaddr_in* from)
{
static int icmpcount = 0;
IpHeader* iphdr = (IpHeader*)buf;

//Number of 32-bit words * 4 = bytes
unsigned short iphdrlen = iphdr->h_len * 4;
DWORD tick = GetTickCount();

if ((iphdrlen == MAX_IP_HDR_SIZE) && (!icmpcount))
{
DecodeIPOptions(buf, bytes);
}

if (bytes < iphdrlen + ICMP_MIN)
{
printf("Too few bytes from %s\n", inet_ntoa(from->sin_addr));
}

IcmpHeader* icmphdr = (IcmpHeader*)(buf + iphdrlen);
if (icmphdr->i_type != ICMP_ECHOREPLY)
{
printf("nonecho type %d recvd\n", icmphdr->i_type);
return;
}

//Make sure this is an ICMP reply to something we sent!
if (icmphdr->i_id != (USHORT)GetCurrentProcessId())
{
printf("someone else's packet!\n");
return;
}

printf("%d bytes from %s:", bytes, inet_ntoa(from->sin_addr));
printf("  icmp_seq = %d. ", icmphdr->i_seq);
printf("  time:%d ms", tick - icmphdr->timestamp);
printf("\n");

icmpcount++;
return;
}

void ValidateArgs(int argc, _TCHAR** argv)
{
lpdest = NULL;
datasize = DEF_PACKET_SIZE;

for (int i = 1; i < argc; i++)
{
if ((argv[i][0] == '-') || (argv[i][0] == '/'))
{
switch (tolower(argv[i][1]))
{
case 'i':
lpdest = argv[i] + 3;
break;
default:
usage();
break;
}
}
else if (isdigit(argv[i][0]))
{
datasize = atoi(argv[i]);
}
}
}

int _tmain(int argc, _TCHAR* argv[])
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("WSAStartup() failed:%d\n", GetLastError());
return -1;
}

ValidateArgs(argc, argv);

SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP,
NULL, 0, WSA_FLAG_OVERLAPPED);
if (sockRaw == INVALID_SOCKET)
{
printf("WSASocket() failed:%d\n", WSAGetLastError());
return -1;
}

//Set the send/recv timeout values
struct sockaddr_in from;
int fromlen = sizeof(from);
int timeout = 1000;

int bread = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO,
(char*)&timeout, sizeof(timeout));
if (bread == SOCKET_ERROR)
{
printf("setsockopt(SO_RCVTIMEO) failed:%d\n", WSAGetLastError());
return -1;
}

timeout = 1000;
bread = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO,
(char*)&timeout, sizeof(timeout));
if (bread == SOCKET_ERROR)
{
printf("setsockopt(SO_SNDTIMEO) failed:%d\n", WSAGetLastError());
return -1;
}

struct sockaddr_in dest;
memset(&dest, 0, sizeof(dest));

//Resolve the endpoint's name if necessary
dest.sin_family = AF_INET;

if ((lpdest != NULL) && strlen(lpdest) != 0)
{
dest.sin_addr.s_addr = inet_addr(lpdest);
}
else
{
struct hostent* hp = gethostbyname(lpdest);
if (hp != NULL)
{
memcpy(&(dest.sin_addr), hp->h_addr, hp->h_length);
dest.sin_family = hp->h_addrtype;
printf("dest.sin_addr = %s\n", inet_ntoa(dest.sin_addr));
}
else
{
printf("gethostbyname() failed:%d\n", WSAGetLastError());
return -1;
}
}

//Create the ICMP packet
datasize += sizeof(IcmpHeader);

char* icmp_data = (char*)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, MAX_PACKET);
if (!icmp_data)
{
printf("HeapAlloc() failed:%d\n", GetLastError());
return -1;
}
memset(icmp_data, 0, MAX_PACKET);
FillICMPData(icmp_data, datasize);

char* recvbuf = (char*)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, MAX_PACKET);

//Start sending/receiving ICMP packets
USHORT seq_no = 0;
while (true)
{
static int nCount = 0;
int bwrote;

if (nCount++ == 4)
{
break;
}

((IcmpHeader*)icmp_data)->i_cksum = 0;
((IcmpHeader*)icmp_data)->timestamp = GetTickCount();
((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)
{
printf("timed out\n");
continue;
}

printf("sendto() failed:%d\n", WSAGetLastError());
return -1;
}

if (bwrote < datasize)
{
printf("Wrote %d bytes\n", bwrote);
}

bread = recvfrom(sockRaw, recvbuf, MAX_PACKET, 0,
(struct sockaddr*)&from, &fromlen);
if (bread == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
printf("timed out\n");
continue;
}

printf("revefrom() failed:%d\n", WSAGetLastError());
return -1;
}

DecodeICMPHeader(recvbuf, bread, &from);

Sleep(1000);
}

//Cleanup
if (sockRaw != INVALID_SOCKET)
{
closesocket(sockRaw);
}

HeapFree(GetProcessHeap(), 0, recvbuf);
HeapFree(GetProcessHeap(), 0, icmp_data);

WSACleanup();

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: