C语言实现PING功能
来源:互联网 发布:程序员试用期转正申请 编辑:程序博客网 时间:2024/06/05 07:13
网络协议分析的一次实验题:ping的原理不再赘述了,直接上代码吧
注:需要ws2_32支持
下载地址:http://download.csdn.net/detail/mass_effect/9867576
#include <iostream>#include <stdio.h>#include <WINSOCK2.H>#include <windows.h>using namespace std;//ICMP回送回应报文类型#define ECHO_REPLY 0//ICMP回送请求报文类型#define ECHO_REQUEST 8//ip首部数据结构struct Ip_Header { //首部长度和版本,高4位为首部长度,低4位为版本 unsigned char ip_verlen; //服务类型/区分服务 unsigned char ip_tos; //总长度 unsigned short ip_total_len; //标识 unsigned short ip_id; //标志&分片偏移 高3位为标志 unsigned short ip_fragoff; //生存时间 unsigned char ip_ttl; //协议 unsigned char ip_proto; //校验和 unsigned short ip_cksum; //源ip地址 unsigned long ip_src_IP; //目的ip地址 unsigned long ip_dst_IP;};//icmp 回送请求和应答报文数据结构struct Icmp_Header { //类型 unsigned char icmp_type; //代码 unsigned char icmp_code; //校验和 unsigned short icmp_cksum; //标识 unsigned short icmp_id; //序列号 unsigned short icmp_seq; //数据部分,这里使用了时间戳 unsigned long icmp_data;};//计算校验和,注意传入的是unsigned char数组, 所以需要两两合并才进行求和unsigned short checkSum(unsigned char *buffer,int size){ unsigned long cksum = 0; unsigned long tmp; while(size>1){ //以16位为单位相加 tmp = *buffer++ << 8; tmp += *buffer++; cksum += tmp; size-=sizeof(unsigned short); } if(size) { //size为奇数的情况 cksum += *(unsigned short*)buffer; } //将溢出的部分和低16位相加,高位溢出添加到低位,与通常的补码运算直接丢弃溢出的高位不同 cksum=(cksum >> 16) + (cksum & 0xffff); //取反返回 return ((unsigned short) ~cksum);}//截取字符串, 得到从offset起的字符串char* subString(char* buffer,int offset) { for(int i = 0 ; i < offset ; buffer++, i++); return buffer;}//字节序转换unsigned short host2net4short(unsigned short value) { return (unsigned short) (((value >> 8) & 0xff) | ((value & 0xff) << 8));}unsigned long host2net4long(unsigned long value) { return (unsigned long) (((value >> 24) & 0xff) | ((value & 0xff) << 24)) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8);}unsigned short net2host4short(unsigned short value) { return host2net4short(value);}unsigned long net2host4long(unsigned long value) { return host2net4long(value);}char* userHelp() { char* buffer = new char[255]; char* command = new char[4]; gets(buffer); strncpy(command, buffer, 4); if(!strcmp(command, "ping")) { return subString(buffer, 5); }else if(!strcmp(command, "help")) { printf("输入格式: ping ip地址/域名\n"); return "?"; }else { printf("您输入的格式错误\n"); return NULL; }}int main(int argc, char* argv[]) { int n; printf("+----------------------Ping小程序----------------------+\n"); printf("| 日期: 2017年6月8日 |\n"); printf("+------------------------------------------------------+\n"); printf("| PS: 输入help命令查看帮助 |\n"); printf("+------------------------------------------------------+\n"); printf("| By:msidolphin |\n"); printf("+------------------------------------------------------+\n"); cout << endl; cout << endl; struct sockaddr_in sa; //socket初始化 WSADATA wsa_data; if (WSAStartup(MAKEWORD(2,2),&wsa_data) != 0){ //代表失败 return -1; } //用户帮助 while(true) { char* address = "127.0.0.1"; do { address = userHelp(); if(address == NULL) { return 0; } }while(!strcmp(address, "?")); HOSTENT *phostent = gethostbyname(address); if(phostent == NULL) { printf("您填写的地址有误!"); return 0; } //一个域名可能对应多个IP地址 for(n=0 ; phostent->h_addr_list[n] ; n++) { memcpy(&sa.sin_addr.s_addr, phostent->h_addr_list[n], phostent->h_length); } sa.sin_family = AF_INET; sa.sin_port = htons(0); //创建原始套接字 //原始套接字可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧(数据包)。另外,必须在管理员权限下才能使用原始套接字。 SOCKET sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if(sock_raw == INVALID_SOCKET) { printf("原始套接字创建失败"); return -1; } printf("正在 Ping [ %s ] 具有 32 字节的数据:\r\n\r\n", inet_ntoa(sa.sin_addr)); //统计未接收到的回送回应报文数量 int lose_packet = 0; sockaddr_in addrRecv; //最大往返时间 int max_time = 0; //最小往返时间 int min_time = 1000; //平均往返时间 int avg_time = 0; //临时变量,用来比较往返时间 int temp_time = 999; //标记是否接收到数据包 bool flag = false; Icmp_Header icmp_packet; //连续发送4个icmp报文 for(int i = 0 ; i < 4 ; ++i) { //icmp 回送请求报文初始化 icmp_packet.icmp_type = ECHO_REQUEST; icmp_packet.icmp_code = 0x00; icmp_packet.icmp_cksum = 0x0000; icmp_packet.icmp_data = (unsigned long) ::GetTickCount(); icmp_packet.icmp_data = host2net4long(icmp_packet.icmp_data); //连续发送数据包是处于同一个进程,所以4次封包的进程号是一致的 icmp_packet.icmp_id = (unsigned short) GetCurrentProcessId(); icmp_packet.icmp_id = host2net4short(icmp_packet.icmp_id); //seqence值用来区分不同的请求 icmp_packet.icmp_seq = (unsigned short) (i+1); icmp_packet.icmp_seq = host2net4short(icmp_packet.icmp_seq); unsigned char temp[40]; memset(temp, 0, 40); //将结构体中的数据拷贝到temp数组中,因为checkSum()函数接收的是unsigned char数组 memcpy(temp, &icmp_packet, 40); icmp_packet.icmp_cksum = host2net4short(checkSum(temp, 40)); memcpy(temp, &icmp_packet, 40); //等待1秒再发送下一个包 Sleep(1000); //发送 sendto(sock_raw, (char*) temp, sizeof(temp), 0, (sockaddr*)&sa, sizeof(sa)); //通过选择模型,设置等待时间 fd_set fd; FD_ZERO(&fd); FD_SET(sock_raw, &fd); //设定超过2秒为超时 timeval tv = {2, 0}; int nResult = select(0, &fd, NULL, NULL, &tv); if (nResult == 0){ lose_packet ++; printf("请求超时...\n"); continue; } //接收数据包 unsigned char recv_packet[MAXBYTE]; int recv_add_len = sizeof(addrRecv); recvfrom(sock_raw, (char*) recv_packet, sizeof(recv_packet), 0, (sockaddr *)&addrRecv, &recv_add_len); //获取ip数据包 Ip_Header *ip_header = (Ip_Header*) recv_packet; //获取icmp 回送应答报文 Icmp_Header *icmp_header = (Icmp_Header*) (recv_packet + 20); memcpy(temp, icmp_header, 40); //验证校验和 if(!checkSum(temp, sizeof(temp))) { if(icmp_header->icmp_type == ECHO_REPLY) { flag = true; unsigned long current_time = ::GetTickCount(); icmp_header->icmp_data = net2host4long(icmp_header->icmp_data); //计算发送和接收往返时间 int interval = current_time - icmp_header->icmp_data - 1000; //累计往返时间 avg_time += interval; //得到最大和最小往返时间 if(i == 0) { temp_time = interval; } if(interval > max_time) { max_time = interval; }else { temp_time = interval; } if(temp_time < min_time) { min_time = temp_time; } if(interval < 1 && interval >= 0) { //如果间隔时间小于1ms,以时间<1ms形式输出 printf("来自 %s 的回复: 字节=%d 时间<1ms TTL=%d\n" ,inet_ntoa(addrRecv.sin_addr) ,sizeof(icmp_header->icmp_data)*8 ,ip_header->ip_ttl ); }else { printf("来自 %s 的回复: 字节=%d 时间=%dms TTL=%d\n" ,inet_ntoa(addrRecv.sin_addr) ,sizeof(icmp_header->icmp_data)*8 ,interval ,ip_header->ip_ttl ); } }else { lose_packet ++; printf("目标不可达...\n"); } }else { lose_packet ++; } } cout << endl; //统计收发信息 printf("%s 的 Ping 统计信息:\n", inet_ntoa(sa.sin_addr)); printf("\t数据包: 已发送 = 4 , 已接收 = %d, 丢失 = %d (%d%%丢失)\n" ,4 - lose_packet ,lose_packet ,(lose_packet)*100 / 4 ); if(flag) { printf("往返行程的估计时间(以毫秒为单位):\n"); avg_time /= (4 - lose_packet); printf("\t最短 = %dms, 最长 = %dms, 平均 = %dms\n" ,min_time ,max_time ,avg_time ); } cout << endl; }}
运行结果:
不足之处:没有对ICMP差错报告报文进行处理
阅读全文
1 0
- C语言实现PING功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 用C语言实现Ping程序功能
- 线性代数笔记-5 转置、置换、向量空间
- PLC编程设备服务处理的高速化趋势简介
- css实现文本超出省略号代替
- MySQL的InnoDB引擎和MyISAM引擎对比
- 使用WebRTC搭建前端视频聊天室——入门篇
- C语言实现PING功能
- Codevs : 1033 蚯蚓的游戏问题 (费用流
- notifyDataSetChanged()无效原因
- milk3-section1.4
- Http协议基础之HTTP请求首部字段
- HTTP协议学习笔记01
- HDU-5981-Guess the number-规律
- 泡影产品需求设计文档
- bzoj3122 随机数生成器 BSGS+费马小定理求逆元