ping工具的C语言简单实现 (ICMP echo)

来源:互联网 发布:焊接机器人编程 编辑:程序博客网 时间:2024/06/08 07:18

Windows和Linux 平台下都有一个使用非常频繁的工具: ping

此工具主要是检验网络中两节点间传递的数据包是否可达,计算耗时等,其原理是使用ICMP协议发送echo,并得到目的主机的回显。

以下是实现了最基本ping 功能的C程序,一共有三个文件:

icmphd.h  —— 定义报文结构

icmphd.c ——实现其声明的函数

main.c —— 主函数入口


编译环境:

操作系统: win7 sp1

CPU :x86_64

编译器:

1) MinGW-GCC version 4.8.1 (tdm64-2)

2) Microsoft Visual Studio Ultimate 2012 version 11.0.50727.1 RTMREL

注:若使用的是MinGW-GCC,则编译时连接库: -lwsock32,否则因为编译器对函数的默认调用规则不同,无法在Windows SDK中找到

导出的符号(符号不匹配)。运行与调试时使用管理员权限,否则发送时报错,WinSock Error Code = 10013


源代码:

icmphd.h

/*  author : ez  date : 2014/10/20  describe : -*/#ifndef _ICMPHD_H_#define _ICMPHD_H_#include <stdint.h>#define DEF_DATA_LEN    0x10// random number// #define DEF_IDENTIFY0x0003#pragma pack (1)struct iphd {uint8_t m_cVersionAndHeaderLen;uint8_t m_cTypeOfService;uint16_t m_sTotalLenOfPacket;uint16_t m_sPacketID;uint16_t m_sSliceinfo;uint8_t m_cTTL;uint8_t m_cTypeOfProtocol;uint16_t m_sCheckSum;uint32_t m_uiSourIp;uint32_t m_uiDestIp;};struct icmphd {uint8_t type;uint8_t code;uint16_t chksum;uint16_t identifier;uint16_t seqnum;    // big-endian};struct icmppk {struct icmphd hd;uint8_t data [DEF_DATA_LEN];};#pragma pack ()#define ICMPHD_SIZE     (sizeof (struct icmphd))#define ICMPPK_SIZE     (sizeof (struct icmppk))#define IPHD_SIZE(sizeof (struct iphd))extern struct icmppk* create_icmppk (uint8_t, uint8_t, uint16_t);#endif // ~ _ICMPHD_H_

icmphd.c

/*  author : ez  date : 2014/10/20  describe : -*/#include "icmphd.h"#include <stdlib.h>#include <string.h>#include <stdio.h>static uint16_t  icmp_cal_cksum(uint8_t*, int);struct icmppk*create_icmppk (uint8_t _type, uint8_t _code, uint16_t _seq) {struct icmppk* pk = (struct icmppk*) malloc (ICMPPK_SIZE);if (pk != NULL) {int i = 0;memset (pk, 0, ICMPPK_SIZE);pk -> hd.type = _type;pk -> hd.code = _code;pk -> hd.identifier = 1;// big-endian((uint8_t*) (&(pk -> hd.seqnum))) [0] = (uint8_t) ((_seq & 0xff00) >> 8);((uint8_t*) (&(pk -> hd.seqnum))) [1] = _seq & 0x00ff;for (; i < DEF_DATA_LEN; i ++)   // random datapk -> data [i] = i + '0';pk -> hd.chksum = icmp_cal_cksum ((uint8_t*) pk, ICMPPK_SIZE);}return pk;}static uint16_t  icmp_cal_cksum(uint8_t* _data,int _data_len) {    int sum = 0;      int odd = _data_len & 0x01;      uint16_t* value = (uint16_t*) _data;      while (_data_len & 0xfffe) {          sum += *(uint16_t*) _data;          _data += 2;        _data_len -= 2;    }    if (odd) {        uint16_t tmp = ((*_data) << 8) & 0xff00;          sum += tmp;      }      sum = (sum >> 16) + (sum & 0xffff);      sum += (sum >> 16);      return ~sum;  }  

main.c

/*  author : ez  date : 2015/7/11 describe : -*/#undef UNICODE#include "icmphd.h"#include <stdlib.h>#include <string.h>#include <stdio.h>#include <winsock2.h>#include <time.h>#if defined _WIN32 || defined _WIN64#include <windows.h> #endif #if !defined MAKEWORD#define MAKEWORD(low,high) ((WORD)(((BYTE)(low)) | ((WORD)((BYTE)(high))) << 8))#endif#pragma comment (lib, "ws2_32.lib")#define ADDR_OFFSET   1#define println(___STR)     printf ("%s\n", ___STR)uint16_t sequence = 0;time_t start = 0, end = 0;uint32_t ip_num = 0;char* ip_char = NULL;struct icmppk* send_data;uint8_t* rcv_data;SOCKET sockfd = INVALID_SOCKET;struct sockaddr_in local;static struct icmppk*request_echo_icmp (/*const char* */);static voiddispose_resources ();static voidparse_reply_echo_icmp (struct icmppk*, void*);int ipstr_to_numeric (const char*, int *);staticvoid usage_disp () {printf ("Usage: ping <ip_address>\n");}static struct icmppk*request_echo_icmp (/*const char* _dst_addr */) {uint16_t ip_numeric = 0;struct icmppk* hd = NULL; // create icmp requesthd = create_icmppk (8, 0, ++ sequence);return hd;}static voidrcv_parse_reply_echo_icmp () {int rcv_len = 0;int frm_len = ICMPPK_SIZE;while (1) {rcv_len = recvfrom (sockfd, (char*) rcv_data, 0x100, 0,(struct sockaddr*) &local, &frm_len);if (rcv_len > 0) {struct icmppk* ptr = (struct icmppk*) (((uint8_t*) rcv_data) + IPHD_SIZE);if (((struct iphd*) rcv_data) -> m_uiSourIp == ip_num &&((struct iphd*) rcv_data) -> m_cTypeOfProtocol == IPPROTO_ICMP) {end = time (NULL);printf ("Reply from %s: bytes = %d, time = %d nm, req = %d, TTL = %d\n", ip_char,DEF_DATA_LEN, difftime (end, start),ntohs (ptr -> hd.seqnum),((struct iphd*) rcv_data) -> m_cTTL);start = end = 0;#ifdef  _MSC_VERSleep (1000);#elif  __GNUC__sleep (1);#endifbreak;} else continue;} else /*if (rcv_len == 0)*/continue;// else  // break;}}static voiddisplay_frame (struct icmppk* _pk, int _len) {int i = 0;for (; i < _len; i ++)printf ("0x%02x ", ((uint8_t*) _pk) [i]);}int ipstr_to_numeric (const char* _str, int * _addr) {const char* index;unsigned char* addr = (unsigned char*) _addr;int isnumeric = 1;int i = 3;*_addr = 0;index = _str;while  ((*index) && (isnumeric))  {if (isdigit  ((unsigned char) *index))addr [i] = addr [i] * 10 + (*index  - '0');  // big-endianelse if (*index == '.') {i --;if (i == -1) isnumeric = 0;} else isnumeric = 0;index ++;}if (isnumeric && i)  return -1;   // errorif (isnumeric)  return 0; // successful}int main (int argc, char *argv []) {            WSADATA wsaData;    struct sockaddr_in hints;int remte_addr = 0;int timeout = 1000;    int res;if (argc < 2) {usage_disp ();return 0;}      // Initialize Winsock    res = WSAStartup (MAKEWORD (2,2), &wsaData);      if (res != 0) {        printf("WSAStartup fault, error: %d\n", res);        return 1;    }rcv_data = (uint8_t*) malloc (0x100);    memset (&hints, 0, sizeof (struct sockaddr_in)); memset (&local, 0, sizeof (struct sockaddr_in));    hints.sin_family = AF_INET;hints.sin_addr.s_addr = ip_num = inet_addr (ip_char = argv [ADDR_OFFSET]);local.sin_family = AF_INET;local.sin_addr.s_addr = inet_addr ("127.0.0.1");      sockfd = socket (AF_INET, SOCK_RAW,        IPPROTO_ICMP);    if (sockfd == INVALID_SOCKET) {        printf ("Socket fault, error: %ld\n", WSAGetLastError ());          WSACleanup ();dispose_resources ();        return 1;    }printf ("Ping host %s\n", ip_char);  while (1) {send_data = request_echo_icmp ();start = time (NULL);// send data to serverres = sendto (sockfd, (const char*) send_data, ICMPPK_SIZE, 0,(struct sockaddr*) &hints, sizeof (hints));if (res == SOCKET_ERROR) {printf ("Send Error : %d\n", WSAGetLastError ());closesocket (sockfd);WSACleanup ();dispose_resources ();return 1;}// receive from remotercv_parse_reply_echo_icmp ();}      // shutdown (sockfd, SD_BOTH);    closesocket (sockfd);    WSACleanup(); dispose_resources ();        return 0;  }static voiddispose_resources () {rcv_data && ( free (rcv_data), (rcv_data = NULL));send_data && ( free (send_data), (send_data = NULL));}
当然因为写的有点匆忙,很多地方还需要改进,本想做成跨平台的(Windows与Linux),也只写了一点点。还有输入的内容没有过滤等

很多问题,不过算是基本能用,下图是我截取的,在我的电脑上运行的结果。欢迎提出宝贵的意见。


0 0