windowsw网络编程 ping程序的实现

【实验目的】
1.熟悉原始套接字编程的基本流程
2.理解ping程序的实现机制
3.理解ICMP协议的基于作用和报文格式。
4.完成原始套接字的配置。

【实验内容】
1.构造ICMP协议首部结构
2.构造ICMP回射请求结构。
3.构造ICMP回射应答结构。
4.构造IP首部结构。
5.创建原始套接字。
6.根据用户指定的地址获取目标IP。
7.对目标地址循环发送ICMP请求。
8.等待对方响应,并计算时间间隔。

源代码:
ping.h

#pragma pack(1)

#define ICMP_ECHOREPLY	0
#define ICMP_ECHOREQ	8

// IP Header -- RFC 791
typedef struct tagIPHDR
{
    
    
	u_char  VIHL;			// Version and IHL
	u_char	TOS;			// Type Of Service
	short	TotLen;			// Total Length
	short	ID;				// Identification
	short	FlagOff;		// Flags and Fragment Offset
	u_char	TTL;			// Time To Live
	u_char	Protocol;		// Protocol
	u_short	Checksum;		// Checksum
	struct	in_addr iaSrc;	// Internet Address - Source
	struct	in_addr iaDst;	// Internet Address - Destination
}IPHDR, * PIPHDR;


// ICMP Header - RFC 792
typedef struct tagICMPHDR
{
    
    
	u_char	Type;			// Type
	u_char	Code;			// Code
	u_short	Checksum;		// Checksum
	u_short	ID;				// Identification
	u_short	Seq;			// Sequence
	char	Data;			// Data
}ICMPHDR, * PICMPHDR;


#define REQ_DATASIZE 32		// Echo Request Data size

// ICMP Echo Request
typedef struct tagECHOREQUEST
{
    
    
	ICMPHDR icmpHdr;
	DWORD	dwTime;
	char	cData[REQ_DATASIZE];
}ECHOREQUEST, * PECHOREQUEST;


// ICMP Echo Reply
typedef struct tagECHOREPLY
{
    
    
	IPHDR	ipHdr;
	ECHOREQUEST	echoRequest;
	char    cFiller[256];
}ECHOREPLY, * PECHOREPLY;

#pragma pack()

ping.c

//
// PING.C -- Ping program using ICMP and RAW Sockets
//
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
//#include <winsock.h>
#include <WinSock2.h>
#pragma comment (lib,"Ws2_32.lib")
#include "ping.h"

// Internal Functions
void Ping(LPCSTR pstrHost);
void ReportError(LPCSTR pstrFrom);
int  WaitForEchoReply(SOCKET s);
u_short in_cksum(u_short* addr, int len);

// ICMP Echo Request/Reply functions
int		SendEchoRequest(SOCKET, LPSOCKADDR_IN);
DWORD	RecvEchoReply(SOCKET, LPSOCKADDR_IN, u_char*);


// main()
void main(int argc, char** argv)
{
    
    
	WSADATA wsaData;
	WORD wVersionRequested = MAKEWORD(1, 1);
	int nRet;

	// Check arguments
	if (argc != 2)
	{
    
    
		fprintf(stderr, "\nUsage: ping hostname\n");
		return;
	}

	// Init WinSock
	nRet = WSAStartup(wVersionRequested, &wsaData);
	if (nRet)
	{
    
    
		fprintf(stderr, "\nError initializing WinSock\n");
		return;
	}

	// Check version
	if (wsaData.wVersion != wVersionRequested)
	{
    
    
		fprintf(stderr, "\nWinSock version not supported\n");
		return;
	}

	// Go do the ping
	Ping(argv[1]);

	// Free WinSock
	WSACleanup();
}


// Ping()
// Calls SendEchoRequest() and
// RecvEchoReply() and prints results
void Ping(LPCSTR pstrHost)
{
    
    
	SOCKET	  rawSocket;
	LPHOSTENT lpHost;
	struct    sockaddr_in saDest;
	struct    sockaddr_in saSrc;
	DWORD	  dwTimeSent;
	DWORD	  dwElapsed;
	u_char    cTTL;
	int       nLoop;
	int       nRet;

	// Create a Raw socket
	rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
	if (rawSocket == SOCKET_ERROR)
	{
    
    
		ReportError("socket()");
		return;
	}

	// Lookup host
	lpHost = gethostbyname(pstrHost);
	if (lpHost == NULL)
	{
    
    
		fprintf(stderr, "\nHost not found: %s\n", pstrHost);
		return;
	}

	// Setup destination socket address
	saDest.sin_addr.s_addr = *((u_long FAR*) (lpHost->h_addr));
	saDest.sin_family = AF_INET;
	saDest.sin_port = 0;

	// Tell the user what we're doing
	printf("\nPinging %s [%s] with %d bytes of data:\n",
		pstrHost,
		inet_ntoa(saDest.sin_addr),
		REQ_DATASIZE);

	// Ping multiple times
	for (nLoop = 0; nLoop < 4; nLoop++)
	{
    
    
		// Send ICMP echo request
		SendEchoRequest(rawSocket, &saDest);

		// Use select() to wait for data to be received
		nRet = WaitForEchoReply(rawSocket);
		if (nRet == SOCKET_ERROR)
		{
    
    
			ReportError("select()");
			break;
		}
		if (!nRet)
		{
    
    
			printf("\nTimeOut");
			break;
		}

		// Receive reply
		dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);

		// Calculate elapsed time
		dwElapsed = GetTickCount() - dwTimeSent;
		printf("\nReply from: %s: bytes=%d time=%ldms TTL=%d",
			inet_ntoa(saSrc.sin_addr),
			REQ_DATASIZE,
			dwElapsed,
			cTTL);
	}
	printf("\n");
	nRet = closesocket(rawSocket);
	if (nRet == SOCKET_ERROR)
		ReportError("closesocket()");
}


// SendEchoRequest()
// Fill in echo request header
// and send to destination
int SendEchoRequest(SOCKET s, LPSOCKADDR_IN lpstToAddr)
{
    
    
	static ECHOREQUEST echoReq;
	static nId = 1;
	static nSeq = 1;
	int nRet;

	// Fill in echo request
	echoReq.icmpHdr.Type = ICMP_ECHOREQ;
	echoReq.icmpHdr.Code = 0;
	echoReq.icmpHdr.Checksum = 0;
	echoReq.icmpHdr.ID = nId++;
	echoReq.icmpHdr.Seq = nSeq++;

	// Fill in some data to send
	for (nRet = 0; nRet < REQ_DATASIZE; nRet++)
		echoReq.cData[nRet] = ' ' + nRet;

	// Save tick count when sent
	echoReq.dwTime = GetTickCount();

	// Put data in packet and compute checksum
	echoReq.icmpHdr.Checksum = in_cksum((u_short*)& echoReq, sizeof(ECHOREQUEST));

	// Send the echo request  								  
	nRet = sendto(s,						/* socket */
		(LPSTR)& echoReq,			/* buffer */
		sizeof(ECHOREQUEST),
		0,							/* flags */
		(LPSOCKADDR)lpstToAddr, /* destination */
		sizeof(SOCKADDR_IN));   /* address length */

	if (nRet == SOCKET_ERROR)
		ReportError("sendto()");
	return (nRet);
}


// RecvEchoReply()
// Receive incoming data
// and parse out fields
DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char* pTTL)
{
    
    
	ECHOREPLY echoReply;
	int nRet;
	int nAddrLen = sizeof(struct sockaddr_in);

	// Receive the echo reply	
	nRet = recvfrom(s,					// socket
		(LPSTR)& echoReply,	// buffer
		sizeof(ECHOREPLY),	// size of buffer
		0,					// flags
		(LPSOCKADDR)lpsaFrom,	// From address
		&nAddrLen);			// pointer to address len

// Check return value
	if (nRet == SOCKET_ERROR)
		ReportError("recvfrom()");

	// return time sent and IP TTL
	*pTTL = echoReply.ipHdr.TTL;
	return(echoReply.echoRequest.dwTime);
}

// What happened?
void ReportError(LPCSTR pWhere)
{
    
    
	fprintf(stderr, "\n%s error: %d\n",
		WSAGetLastError());
}


// WaitForEchoReply()
// Use select() to determine when
// data is waiting to be read
int WaitForEchoReply(SOCKET s)
{
    
    
	struct timeval Timeout;
	fd_set readfds;

	readfds.fd_count = 1;
	readfds.fd_array[0] = s;
	Timeout.tv_sec = 5;
	Timeout.tv_usec = 0;

	return(select(1, &readfds, NULL, NULL, &Timeout));
}


//
// Mike Muuss' in_cksum() function
// and his comments from the original
// ping program
//
// * Author -
// *	Mike Muuss
// *	U. S. Army Ballistic Research Laboratory
// *	December, 1983

/*
 *			I N _ C K S U M
 *
 * Checksum routine for Internet Protocol family headers (C Version)
 *
 */
u_short in_cksum(u_short* addr, int len)
{
    
    
	register int nleft = len;
	register u_short* w = addr;
	register u_short answer;
	register int sum = 0;

	/*
	 *  Our algorithm is simple, using a 32 bit accumulator (sum),
	 *  we add sequential 16 bit words to it, and at the end, fold
	 *  back all the carry bits from the top 16 bits into the lower
	 *  16 bits.
	 */
	while (nleft > 1) {
    
    
		sum += *w++;
		nleft -= 2;
	}

	/* mop up an odd byte, if necessary */
	if (nleft == 1) {
    
    
		u_short	u = 0;

		*(u_char*)(&u) = *(u_char*)w;
		sum += u;
	}

	/*
	 * add back carry outs from top 16 bits to low 16 bits
	 */
	sum = (sum >> 16) + (sum & 0xffff);	/* add hi 16 to low 16 */
	sum += (sum >> 16);			/* add carry */
	answer = ~sum;				/* truncate to 16 bits */
	return (answer);
}


更改ping调试信息
在这里插入图片描述
运行效果:
在这里插入图片描述
icmp抓包效果截图:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Gunanhuai/article/details/109815129