1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > C语言实现PING功能

C语言实现PING功能

时间:2022-07-16 03:49:36

相关推荐

C语言实现PING功能

网络协议分析的一次实验题:ping的原理不再赘述了,直接上代码吧

注:需要ws2_32支持

下载地址:/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("| 日期: 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差错报告报文进行处理

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。