ping工具的C語言簡單實現 (ICMP echo)
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_IDENTIFY 0x0003
#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 data
pk -> 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 void
dispose_resources ();
static void
parse_reply_echo_icmp (struct icmppk*, void*);
int
ipstr_to_numeric (const char*, int *);
static
void 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 request
hd = create_icmppk (8, 0, ++ sequence);
return hd;
}
static void
rcv_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_VER
Sleep (1000);
#elif __GNUC__
sleep (1);
#endif
break;
} else
continue;
} else /*if (rcv_len == 0)*/
continue;
// else
// break;
}
}
static void
display_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-endian
else if (*index == '.') {
i --;
if (i == -1) isnumeric = 0;
} else
isnumeric = 0;
index ++;
}
if (isnumeric && i) return -1; // error
if (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 server
res = 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 remote
rcv_parse_reply_echo_icmp ();
}
// shutdown (sockfd, SD_BOTH);
closesocket (sockfd);
WSACleanup();
dispose_resources ();
return 0;
}
static void
dispose_resources () {
rcv_data && ( free (rcv_data), (rcv_data = NULL));
send_data && ( free (send_data), (send_data = NULL));
}
當然因為寫的有點匆忙,很多地方還需要改進,本想做成跨平臺的(Windows與Linux),也只寫了一點點。還有輸入的內容沒有過濾等
很多問題,不過算是基本能用,下圖是我擷取的,在我的電腦上執行的結果。歡迎提出寶貴的意見。
相關文章
- windows下ping程式使用C語言實現WindowsC語言
- Linux C++ 實現一個簡易版的ping (也就是ICMP協議)LinuxC++協議
- Windows7/10實現ICMP(ping命令)Windows
- C語言實現的一個簡單的猜數小遊戲C語言遊戲
- C語言實現MD5加密,竟如此簡單!C語言加密
- 力扣896. 單調數列-C語言實現-簡單題力扣C語言
- 騰訊實習筆試題--簡單計算器程式的c語言實現筆試C語言
- 力扣566. 重塑矩陣-C語言實現-簡單題力扣矩陣C語言
- C語言-GCC的簡單介紹C語言GC
- 使用Go語言實現簡單MapReduce框架Go框架
- Go語言實現簡單的反序列化Go
- C語言簡單程式碼程式C語言
- 關於C語言的簡單介紹C語言
- C語言_簡單的階乘函式C語言函式
- C語言實現一個簡易的Hash table(7)C語言
- Linux雜談: 實現一種簡單實用的執行緒池(C語言)Linux執行緒C語言
- 使用 R 語言實現簡單的文字識別程式
- 力扣561. 陣列拆分 I-C語言實現-簡單題力扣陣列C語言
- C語言入門很簡單pdfC語言
- 力扣485. 最大連續1的個數-C語言實現-簡單題力扣C語言
- 簡單介紹VBS 批次Ping的專案實現
- C語言呼叫mysql資料庫API實現簡單的mysql客戶端的功能C語言MySql資料庫API客戶端
- 有用的C語言工具C語言
- C語言練手專案--C 語言製作簡單計算器C語言
- 掃雷--C語言實現C語言
- C語言實現DES加密C語言加密
- c語言實現階乘C語言
- 簡單而重要的協議:ICMP協議
- 多語言版vfp程式設計簡單實現 (轉)程式設計
- C語言,實現數字譜到簡譜的轉換(二)C語言
- c語言關於陣列的簡單運算C語言陣列
- C語言 簡單的佇列(陣列佇列)C語言佇列陣列
- C 語言標頭檔案作用的簡單理解
- c語言單向連結串列逆轉實現方法C語言
- mustafaquraish/cup:簡單的像C一樣的程式語言AI
- go語言實現的一款簡單的網盤系統Go
- C語言實現TCP通訊C語言TCP
- 高精度加法(C語言實現)C語言