ping程序---unix系统

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

#define PACKLEN 64	

unsigned short Chksum(unsigned short *ptr,int size);
void Set_header(int seq,char *datapack);
int Unpack(char *pack,int size);
void statistics(int signo);

int sendseq = 0,recvseq = 0;
int sockfd;
struct sockaddr_in out;
struct sockaddr_in in;
int iplen,inlen;
char sendbuf[4096];
char recvbuf[4096];

int main(int argc,char *argv[])
{
	//接收control-c信号
	signal(SIGINT,statistics);

	out.sin_family = AF_INET;
	out.sin_addr.s_addr = inet_addr(argv[1]);
	//创建ICMP原始套接字,需要root权限
	if((sockfd = socket(AF_INET,SOCK_RAW,IPPROTO_ICMP)) < 0)
	{
		perror("socket error.");
		exit(1);
	}

	printf("PING %s(%s): %d bytes data in ICMP packets.\n",argv[1],inet_ntoa(out.sin_addr),PACKLEN - 8);
	while(1)
	{
		bzero(sendbuf,sizeof(sendbuf));
		//设置报头
		Set_header(sendseq,sendbuf);
		sendseq++;
		//发送数据包
		if(sendto(sockfd,sendbuf,PACKLEN,0,(struct sockaddr*)&out,sizeof(out)) < 0)
		{
			perror("sendto error.");
			exit(1);
		}
		bzero(recvbuf,sizeof(recvbuf));
		bzero(&in,sizeof(in));
		inlen = sizeof(in);
		//接收数据包
		if((iplen = recvfrom(sockfd,recvbuf,sizeof(recvbuf),0,(struct sockaddr*)&in,&inlen)) <= 0)
		{
			perror("recvfrom error.");
			exit(1);
		}
		//解包,recvfrom从SOCK_RAW类型套接字接收到的数据为IP包
		if( Unpack(recvbuf,iplen) == -1)
		{
			printf("-------Error---------\n");
		}
		else
			recvseq++;
		sleep(1);

	}
	


	return 0;
}

void statistics(int signo)
{
	printf("\n--------------PING statistics-------------\n");
	printf("%d packets transmitted, %d received	,%%%.2f lost\n",sendseq,recvseq,(double)(sendseq-recvseq) / sendseq * 100);
	close(sockfd);
	exit(1);
}
//计算校验和
unsigned short Chksum(unsigned short *ptr,int size)
{
	int sum = 0;

	//以两个字节为单位累加
	while(size > 1)
	{
		sum += *ptr;
		ptr++;
		size -= 2;
	}
	
	//剩余一个字节放在高位继续累加
	if(size)
		sum += (*(unsigned char*)ptr) << 8;

	//把16位后的进位加在低位
	while(sum >> 16)
	{
		sum = (sum >> 16) + (sum & 0xffff);
	}
	//取反码
	return (unsigned short)(~sum);
}
//设置ICMP报头
void Set_header(int seq,char *datapack)
{
	struct icmp *icmp;
	struct timeval *time;

	icmp = (struct icmp*)datapack;
	//报文类型:请求回显
	icmp -> icmp_type = ICMP_ECHO;
	icmp -> icmp_code = 0;
	//标识符设为当前进程号
	icmp -> icmp_id = getpid();
	icmp -> icmp_seq = seq;
	icmp -> icmp_cksum = 0;
	
	//把数据去保存为当前时间
	time = (struct timeval*)icmp -> icmp_data;
	gettimeofday(time,NULL);
	
	//计算校验和
	icmp -> icmp_cksum = Chksum((unsigned short*)icmp,PACKLEN);
}
//数据解包
int Unpack(char *pack,int size)
{
	struct ip *ip;
	struct icmp* icmp;
	struct timeval now;
	struct timeval *timeout;
	int iplen,icmplen;
	double rtt = 0;

	ip = (struct ip*)pack;
	//计算ip报头长度
	iplen = ip -> ip_hl * 4;

	icmp = (struct icmp*)(pack + iplen);
	icmplen = size - iplen;
	if(icmplen < 8)
	{
		return -1;
	}
	if((icmp -> icmp_type == ICMP_ECHOREPLY) && (icmp -> icmp_id == getpid()))
	{

		//计算时间 当前时间与存取icmp数据区的发送时间相减
		timeout = (struct timeval*)icmp -> icmp_data;
		gettimeofday(&now,NULL);

		if((now.tv_usec = now.tv_usec - timeout -> tv_usec) < 0)
		{
			now.tv_sec--;
			now.tv_usec + 1000000;
		}
		now.tv_sec -= timeout -> tv_sec;
		rtt = now.tv_sec * 1000 + now.tv_usec / 1000.0;
		printf("%d byte from %s: icmp_seq = %u ttl = %d time = %.3f ms\n",icmplen,inet_ntoa(in.sin_addr),icmp -> icmp_seq,ip -> ip_ttl,rtt);
		return 1;

	}
	//ping 127.0.0.1
	else if((icmp -> icmp_type == ICMP_ECHO) && (icmp -> icmp_id == getpid()))
	{
		bzero(recvbuf,sizeof(recvbuf));
		inlen = sizeof(in);
		//接收数据包
		if((iplen = recvfrom(sockfd,recvbuf,sizeof(recvbuf),0,(struct sockaddr*)&in,&inlen)) <= 0)
		{
			perror("recvfrom error.");
			exit(1);
		}
		if(Unpack(recvbuf,iplen) == -1)
			return -1;
	}
	else
		return -1;


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值