ping C语言实现

这是一个实现简单ping功能的C程序,它利用ICMP协议发送和接收数据包。程序包括计算校验和、打包ICMP数据、发送ICMP请求报文、接收并解析ICMP响应报文等功能。通过多线程分别处理发送和接收操作,同时包含了信号处理以在接收到中断信号时打印统计信息。
摘要由CSDN通过智能技术生成
#include "ping.h"

//icmp包的组成
struct icmp
{
	unsigned char type;
	unsigned char code;
	unsigned short chksum;

	unsigned short id;
	unsigned short seq;

	unsigned long tv_sec;//秒
	unsigned long tv_usec;//微妙

	unsigned char unused[DATASIZE]; 
};
/**********************
函数名:
功能:
参数:
返回值:
**********************/
unsigned short Ping_calchecksum(char *buffer, int len)
{
	unsigned short *p = (unsigned short *)buffer;
	unsigned int sum = 0;
	int i = 0;
	
	for (i = 0; i < len/2; i++) 
	{
		sum += *p;
		p++;
	}
	sum += (sum >> 16);
	return ~sum; 
}

/**********************
函数名:
功能:
参数:
返回值:
**********************/
int Ping_pack(int pack_no)
{
	bzero(&g_icmp_data, sizeof(g_icmp_data));
	g_icmp_data.type = PA_RESQUST;
	g_icmp_data.code = 0;
	g_icmp_data.chksum = 0;
	g_icmp_data.id = 0;
	g_icmp_data.seq = 0;
	struct timeval tv;
	gettimeofday(&tv, NULL); 

	g_icmp_data.tv_sec = tv.tv_sec;
	g_icmp_data.tv_usec = tv.tv_usec;
	memset(g_icmp_data.unused, 0, DATASIZE);

	g_icmp_data.chksum = Ping_calchecksum((char *)&g_icmp_data, sizeof(g_icmp_data));
	
	return 0;
}


/*********************
函数名:Ping_mysend
功能:发送ICMP请求报文
参数:num 发送请求包的次数
返回值:0
**********************/
int Ping_mysend(int num)
{
	int packetsize = 0;
	int i = 0;
	char ch = 0;
	int rlen = 0;

	for (i = 0; i < num; i++)
	{
		packetsize = Ping_pack(i);
		if (sendto(g_sockfd, &g_icmp_data, sizeof(g_icmp_data), 0, (struct sockaddr *)&g_ser, sizeof(g_ser)) < 0)
		{
			perror("sendto");
			continue;
		}
		else
		{
			g_begin++;
			sleep(1);
		}
		
	}
	return 0;
}



void* send_data(void* arg) 
{
	int num = 0;
	
	if (0 == g_array[0])
	{
		Ping_mysend(PING_A);	
	}
	else if (0 == strncmp(g_array, "-a", 2))
	{
		Ping_mysend(PING_A);
			
	}
	else if (0 == strncmp(g_array, "-t", 2))
	{
		Ping_mysend(PING_T);
	}
	else if (0 == strncmp(g_array, "-n", 2))
	{
		num = Ping_gettimeofn();	
		Ping_mysend(num);
	}

	return 	NULL;
}



/**********************
函数名:Ping_gettimeofn
功能:获得ping -n conut 参数conut的值
返回值: sum
**********************/
int Ping_gettimeofn()
{
	int sum = 0;
	
	sum = atoi(g_nconut);
	
	return sum;
}


/**********************
函数名:Ping_myrecv
功能:接收icmp回复报文
参数:num
返回值:接收的次数,依据参数和发送的次数定
**********************/
int Ping_myrecv(int num)
{
	int rlen = 0;
	int i = 0;
	char ch = 0;
	int ret = 0;
	
	while (i < num)
	{
		memset(g_buffer_recv, 0, sizeof(g_buffer_recv));

		rlen = recv(g_sockfd, g_buffer_recv, sizeof(g_buffer_recv), 0);
		if (rlen < 0)
		{
			printf("fail to recv!\n");
			continue;
		}
		i++;	
		ret = Ping_unpack(rlen);
		
		if (-1 == ret)
		{
			continue;
		}
		else
		{
			g_end++;
		}	
	}
	return 0;
}

void* recv_data(void *arg)
{
	int num = 0;
	
	if (0 == g_array[0])
	{
		Ping_myrecv(PING_A);	
	}
	else if (0 == strncmp(g_array, "-a", 2))
	{
		Ping_myrecv(PING_A);
			
	}
	else if (0 == strncmp(g_array, "-t", 2))
	{
		Ping_myrecv(PING_T);
	}
	else if (0 == strncmp(g_array, "-n", 2))
	{
		num = Ping_gettimeofn();
			
		Ping_myrecv(num);
	}
	
	return NULL;
}

/**********************
函数名:Ping_unpack
功能:解析响应数据包、判断是否返回正常
参数:len 响应数据包长度
返回值:成功返回0,失败返回-1
**********************/
int Ping_unpack(int len)
{
	double rtt = 0;
	struct timeval tv_cur;
	struct ip *myip;
	struct icmp *myicmp;
	int ipsize = 0;	
	
	char *recv_ip = NULL;
	
	myip = (struct ip*)g_buffer_recv;
	ipsize = (myip->ip_hl)<<2;
	
	recv_ip = (char *)inet_ntoa(myip->ip_src);
	len -= ipsize;

	myicmp = (struct icmp *)(g_buffer_recv + ipsize);
	if (len < UP_ICMPHEAD)   
	{
		perror("ICMP packets length is less than 8\n");
		return -1;
	}
	else if(strcmp(g_ip, recv_ip)) 
	{
		return -1;
	}	
	
	else if (myicmp->type != 0) 
	{
		return -1;
	}	
	else
	{
		gettimeofday(&tv_cur, NULL);
		rtt = (tv_cur.tv_sec - g_icmp_data.tv_sec)*1000 + (tv_cur.tv_usec - g_icmp_data.tv_usec)/1000.0f;
	
		g_totaltime += rtt;
		if (rtt > g_maxtime)
		{
			g_maxtime = rtt;
		}	  
		if (rtt < g_mintime)
		{
			g_mintime = rtt;
		}

		printf("%d byte from %s: icmp_seq=%u ttl=%d rtt=%0.1fms\n", g_datalen + UP_ICMPHEAD, inet_ntoa(g_ser.sin_addr), g_end,  myip->ip_ttl, rtt);
	}

	return 0;
}

/**********************
函数名:sig_int
功能:收到结束线程信号后,打印数据
参数:signo
**********************/
void sig_int(int signo)
{
	double perce = 0;

	perce = (g_begin - g_end) / g_begin;
	printf("---ping %s is complete--- \n %d was sended and %d was received, %0.1lf%%packet loss, time %0.1fms rtt\n min/avg/max = %0.1f/%0.1f/%0.1f ms \n", g_tmpbuff, g_begin, g_end, perce * 100 , g_totaltime, g_mintime, g_totaltime/g_begin, g_maxtime);

	exit(0);
}

int main(int argc, char *argv[])
{
	double number = 0;
	struct hostent *ent = NULL;
	pthread_t pid1 = 0, pid2 = 0;

	
	signal(SIGINT, sig_int);
	
	snprintf(g_tmpbuff, sizeof(g_tmpbuff), "%s", argv[1]);

	if (argc < 2)
	{
		return -1;
	}
	if (argv[1] == NULL)
	{
		perror("fail to ping!");
		return -1;
	}
	if (argc > 2)
	{
		memset(g_array, 0, sizeof(g_array));
		memset(g_nconut, 0, sizeof(g_nconut));
		snprintf(g_array, sizeof(g_array), "%s", argv[2]);
	}
	if (argc > 3)
	{	
	 	if (0 == strncmp(g_array, "-n", 2))
		{
			snprintf(g_nconut, sizeof(g_nconut), "%s", argv[3]); 
		}
	}	
		
	ent = gethostbyname(argv[1]);
				
	if (NULL == ent)
	{
		printf("ping: unknown host %s\n", argv[1]);
		exit(-1);
	}
			
	inet_ntop(AF_INET, ent->h_addr, g_ip, sizeof(g_ip));  //将二进制的32位ip地址转换成字符串的点分十进制
	
			
			
	g_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
			
	//设置目标地址
	bzero(&g_ser, sizeof(g_ser));
	g_ser.sin_family = AF_INET;
	inet_pton(AF_INET, g_ip, &g_ser.sin_addr); //把点分十进制转化成二进制地址
	

	printf("PING %s (%s) %d bytes of data.\n", g_tmpbuff, g_ip, g_datalen);
	//创建线程进行收发数据
	if(pthread_create(&pid1, NULL, send_data, NULL) < 0)
	{
		 perror("pthread_create");
		 return -1;
	}
	 if(pthread_create(&pid2, NULL, recv_data, NULL) < 0)
	{
		 perror("pthread_create");
		  return -1;
	 }

	pthread_join(pid1, NULL);
	pthread_join(pid2, NULL);
				 
	number = (g_begin - g_end) * 100 / g_begin;
	printf("---ping %s is complete--- \n %d was sended and %d was received, %0.1lf%%packet loss, time %0.1fms \n min/avg/max = %0.1f/%0.1f/%0.1f ms \n", argv[1], g_begin, g_end, number, g_totaltime, g_mintime, g_totaltime/g_begin, g_maxtime);

	close(g_sockfd);
	
	return 0;
}


头文件


#ifndef PING__H_
#define PING__H_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/time.h>
#include <signal.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <pthread.h>

#define BUFFER_SIZE       512
#define MAX_SIZE          56
#define ARRAY_SIZE        12
#define NCONUT_SIZE       12
#define ARGV1             128
#define MINSIZE           1000
#define DATASIZE          48
#define PING_A            4
#define PING_T            100
#define UP_ICMPHEAD       8
#define PA_RESQUST        8

struct icmp g_icmp_data;
struct sockaddr_in g_ser;
int g_begin = 0;
int g_end = 0;
int g_sockfd = 0;
char g_buffer_recv[BUFFER_SIZE] = {0};
int g_datalen = MAX_SIZE;
char g_array[ARRAY_SIZE] = {0};
char g_nconut[NCONUT_SIZE] = {0};
char g_tmpbuff[ARGV1] = {0};
double g_totaltime = 0;
double g_maxtime = 0;
double g_mintime = MINSIZE;
char g_ip[32] = {0};




int Ping_compareInput();
int Ping_gettimeofn();
unsigned short Ping_calchecksum(char *buffer, int len);
int Ping_pack(int pack_no);
int Ping_mysend();
int Ping_myrecv();
int Ping_unpack(int len);

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值