原始套接字编程

实验四 原始套接字编程

一、 实验目的

1.了解Winsock原始套接字编程功能和特点。
2.掌握Winsock原始套接字编程基本方法和步骤。
3.理解ICMP协议在网络中的具体应用及其实现原理。

二、实验内容

1.利用原始套接字编程实现ping命令:
运用原始套接字编程技术,通过ICMP协议所提供的回送请求(echo request)和回送应答(echo reply)报文实现检测目的主机的可达性与状态。
2.(选做)在实现ping命令的基础上,参照教材例6.9设计一个局域网扫描工具。

三、实验原理

Ping的工作原理
ping程序是用来探测主机到主机之间是否可通信,如果不能ping到某台主机,表明不能和这台主机建立连接。ping 使用的是ICMP协议,它发送ICMP回送请求消息给目的主机。ICMP协议规定:目的主机必须返回ICMP回送应答消息给源主机。如果源主机在- -定时间内收到应答,则认为主机可达。
ICMP协议通过IP协议发送的,IP协议是无连接的,不可靠的数据包协议。因此,保证数据送达的工作应该由其他的模块来完成。
使用ping命令可以检测指定设备的在线状态,但在程序中通常不会直接执行ping命令而是通过Socket编程的方式向目标ip地址发送icmp请求包,然后返回等待结果。

四、实验步骤

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

五、实验小结

附:程序源代码

#include "stdafx.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib")
#define WIN32_LEAN_AND_MEAN 
#define IP_RECORD_ROUTE  0x7
#define ICMP_MIN 8
#define ICMP_ECHOREPLY 0
#define ICMP_ECHO 8
 
#define DEF_PACKET_SIZE  32       
#define MAX_PACKET       1024     
#define MAX_IP_HDR_SIZE  60       
 
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;
 
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);
}
 
typedef struct _ihdr {
	BYTE i_type;									
	BYTE i_code;									
	USHORT i_cksum;								
	USHORT i_id;									
	USHORT i_seq;								
	ULONG timestamp;							
}IcmpHeader;
 
void fill_icmp_data(char * icmp_data, int datasize){
 
	IcmpHeader *icmp_hdr;
	char *datapart;

	icmp_hdr = (IcmpHeader*)icmp_data;

	icmp_hdr->i_type = ICMP_ECHO;									
	icmp_hdr->i_code = 0;													
	icmp_hdr->i_id = (USHORT)GetCurrentThreadId();			
	icmp_hdr->i_cksum = 0;													
	icmp_hdr->i_seq = 0;														
	datapart = icmp_data + sizeof(IcmpHeader);					

	memset(datapart, 'E', datasize - sizeof(IcmpHeader));
}
 
int decode_icmp_resp(char *buf, int bytes, sockaddr_in *from, DWORD tid)
{
	IpHeader *iphdr;
	IcmpHeader *icmphdr;
	unsigned short iphdrlen;
	iphdr = (IpHeader*)buf;
	iphdrlen = iphdr->h_len * 4;
	if (bytes < iphdrlen + ICMP_MIN)
	{
		return -1;
	}
	icmphdr = (IcmpHeader*)(buf + iphdrlen);
	if (icmphdr->i_type != ICMP_ECHOREPLY)
	{
		return -2;
	}
	if (icmphdr->i_id != (USHORT)tid)
	{
		return -3;
	}
	int time = GetTickCount() - (icmphdr->timestamp);
	if (time >= 0)
	{
		return time;
	}
	else
	{
		return -4;
	}
}
 
int ping(const char *ip, DWORD timeout)
{
	WSADATA wsa;
	SOCKET sockRaw = NULL;
	sockaddr_in dest, from;
	hostent *hp;
	int datasize;
	char *dest_ip;
	char *icmp_data = NULL;
	char *recvbuf = NULL;
	USHORT seq_no = 0;
	int retval;
	int ret;
	
	if (WSAStartup(MAKEWORD(2, 1), &wsa) != 0){
		ret = -1000;
		goto FIN;
	}

	sockRaw = WSASocket(AF_INET,
		SOCK_RAW,
		IPPROTO_ICMP,
		NULL, 0, WSA_FLAG_OVERLAPPED);
	if (sockRaw == INVALID_SOCKET) {
		ret = -2;
		goto FIN;
	}
	int bread = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
		sizeof(timeout));
	if (bread == SOCKET_ERROR) {
		ret = -3;
		goto FIN;
	}
	bread = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout,
		sizeof(timeout));
	if (bread == SOCKET_ERROR) {
		ret = -4;
		goto FIN;
	}
	memset(&dest, 0, sizeof(dest));
 
	unsigned int addr = 0;					
	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;
	datasize = datasize + 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);
	DWORD startTime = GetTickCount();
	((IcmpHeader*)icmp_data)->timestamp = startTime;
	((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);
	int bwrote;
	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;
	}
	LARGE_INTEGER ticksPerSecond;
	LARGE_INTEGER start_tick;
	LARGE_INTEGER end_tick;
	double elapsed;
	QueryPerformanceFrequency(&ticksPerSecond);
	QueryPerformanceCounter(&start_tick);
	int fromlen = sizeof(from);
	while (1)
	{
		retval = recvfrom(sockRaw, recvbuf, MAX_PACKET, 0, (struct sockaddr*)&from, &fromlen);
		if (retval == SOCKET_ERROR)
		{
			if (WSAGetLastError() == WSAETIMEDOUT)
			{
				ret = -1;
				goto FIN;
			}
			ret = -9;
			goto FIN;
		}
		int time = decode_icmp_resp(recvbuf, retval, &from, GetCurrentThreadId());
		if (time >= 0)
		{
			QueryPerformanceCounter(&end_tick);
			elapsed = ((double)(end_tick.QuadPart - start_tick.QuadPart) / ticksPerSecond.QuadPart);
			ret = (int)(elapsed * 1000);
			goto FIN;
		}
		else if (GetTickCount() - startTime >= timeout || GetTickCount() < startTime)
		{
			ret = -1;
			goto FIN;
		}
	}
FIN:
	closesocket(sockRaw);
	WSACleanup();
	return ret;
}
 
int main(int argc, char **argv) {
	printf("ping %s\n", argv[1]);
	int ret = ping(argv[1], 5000);
	if (ret >= 0)
	{
		printf("%s在线,用时%dms\n", argv[1], ret);
	}
	else
	{
		printf("%d\n", ret);
	}
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半夏风情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值