windows下ping程式使用C語言實現
vc++6.0或者更高版本vs新建win32 console專案,選簡單的Hello world專案,刪除自動生成的程式碼,增加如下程式碼,連結(F7)(不要執行)後在該專案的Debug目錄下使用命令列方式執行程式。
// iping.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#pragma comment(lib,"ws2_32.lib")
//#pragma pack(4) //位元組對齊
#include "winsock2.h"
#include "stdlib.h"
#include "stdio.h"
#define ICMP_ECHO 8 //ICMP回顯請求
#define ICMP_ECHOREPLY 0 //ICMP回顯應答
#define ICMP_MIN 8 //ICMP資料包最短為8個位元組
#define DEF_PACKET_SIZE 32 //預設資料包長度
#define DEF_PACKET_NUMBER 4 //預設傳送ICMP請求的次數
#define MAX_PACKET 1024 //資料包最大長度
//定義IP頭部
typedef struct iphdr
{
unsigned int h_len:4; // 頭部長
unsigned int version:4; // 版本號
unsigned char tos; // 服務型別
unsigned short total_len; // 總長度
unsigned short ident; // 標識
unsigned short frag_and_flags; //標誌
unsigned char ttl; //生存時間
unsigned char proto; // 上層協議
unsigned short checksum; // 校驗和
unsigned int sourceIP; //源IP
unsigned int destIP; //目的IP
}IpHeader;
// 定義ICMP 頭部
typedef struct icmphdr
{
BYTE i_type; //型別
BYTE i_code; //程式碼
USHORT i_cksum; //校驗和
USHORT i_id; //標識
USHORT i_seq; //序列號
ULONG timestamp; //資料
}IcmpHeader;
void fill_icmp_data(char *, int); //填充icmp資料包
USHORT checksum(USHORT *, int); //計算校驗和
int decode_resp(char *,int ,struct sockaddr_in *); //收到資料後解碼
void Usage(char *progname)//提示使用者該程式使用方法
{
printf("Usage:\n");
printf("%s target [number of packets] [data_size]\n",progname);
printf("datasize can be up to 1Kb\n");
}
void main(int argc, char **argv)
{
WSADATA wsaData; //初始化windows socket需要的引數
SOCKET sockRaw; //原始套接字
struct sockaddr_in dest,from; //源、目的IP地址
struct hostent * hp; //指標指向包含主機名、地址列表等資訊的結構體
int iRecv,iSend, datasize,times;
int fromlen = sizeof(from);
int timeout = 1000; //超時時間1000ms=1s
int statistic = 0; // 用於統計
char *dest_ip;
char *icmp_data;
char *recvbuf;
unsigned int addr=0;
USHORT seq_no = 0;
int i;
if (WSAStartup(MAKEWORD(2,1),&wsaData) != 0)
{
printf("WSAStartup failed: %d\n",GetLastError());
return;
}
//使用方法不對時顯示提示資訊
if (argc <2 )
{
Usage(argv[0]);
return;
}
//建立原始套接字
// sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, WSA_FLAG_OVERLAPPED);
//注:為了使用傳送接收超時設定(即設定SO_RCVTIMEO, SO_SNDTIMEO),
// 必須將標誌位設為WSA_FLAG_OVERLAPPED !
sockRaw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
// 建立原始套接字不成功
if (sockRaw == INVALID_SOCKET)
{
printf("WSASocket() failed: %d\n", WSAGetLastError());
return;
}
//設定傳送超時時間
iRecv = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
if(iRecv == SOCKET_ERROR)
{
printf("failed to set recv timeout: %d\n",WSAGetLastError());
return;
}
//設定接收資料超時時間
timeout = 1000;
iRecv = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
if(iRecv == SOCKET_ERROR) {
printf("failed to set send timeout: %d\n",WSAGetLastError());
return;
}
memset(&dest,0,sizeof(dest));
//解析使用者輸入的目標地址
hp = gethostbyname(argv[1]);
if (!hp)
{
addr = inet_addr(argv[1]);
}
//非法輸入
if ((!hp) && (addr == INADDR_NONE))
{
printf("Unable to resolve %s\n",argv[1]);
return;
}
//記錄目標主機資訊的結構體
//地址
if (hp != NULL)
memcpy(&(dest.sin_addr),hp->h_addr,hp->h_length);
else
dest.sin_addr.s_addr = addr;
//協議族
if (hp)
dest.sin_family = hp->h_addrtype;
else
dest.sin_family = AF_INET;
//目標IP
dest_ip = inet_ntoa(dest.sin_addr);
//除了目標地址,還給出了Ping的次數
if(argc>2)
{
times=atoi(argv[2]);
if(times == 0)
times = DEF_PACKET_NUMBER;
}
else
times = DEF_PACKET_NUMBER;
//還給出了資料大小
if (argc >3)
{
datasize = atoi(argv[3]);
//給的是0,則用預設資料包大小
if (datasize == 0)
datasize = DEF_PACKET_SIZE;
//使用者給出的資料包大小太大
if (datasize >1024)
{
printf("WARNING : data_size is too large !\n");
datasize = DEF_PACKET_SIZE;
}
}
else
datasize = DEF_PACKET_SIZE;
datasize += sizeof(IcmpHeader);
icmp_data = (char *)malloc(MAX_PACKET);
recvbuf = (char *)malloc(MAX_PACKET);
if (!icmp_data)
{
printf("HeapAlloc failed %d\n",GetLastError());
return;
}
memset(icmp_data, 0, MAX_PACKET);
//填充ICMP資料包,型別、程式碼、標識等
fill_icmp_data(icmp_data,datasize);
//提示正在ping目標主機
printf("\nPinging %s ....\n\n",dest_ip);
//Ping多次
for(i=0; i<times; i++)
{
//準備ICMP包頭部資料
((IcmpHeader *)icmp_data)->i_cksum = 0;
//取得以毫秒為單位的計算機啟動後經歷的時間間隔
((IcmpHeader *)icmp_data)->timestamp = GetTickCount();
((IcmpHeader *)icmp_data)->i_seq = seq_no++; //序列號遞增
((IcmpHeader *)icmp_data)->i_cksum = checksum((USHORT*)icmp_data,datasize);//計算校驗和
//傳送ICMP資料包
iSend = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest));
//傳送失敗
if (iSend == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
printf("Request timed out.\n");
continue;
}
printf("sendto failed: %d\n",WSAGetLastError());
break;
}
if (iSend < datasize )
{
printf("Only sent %d bytes\n",iSend);
}
//接收應答資料
iRecv = recvfrom(sockRaw, recvbuf, MAX_PACKET, 0, (struct sockaddr*)&from, &fromlen);
//接收失敗
if (iRecv == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
printf("Request timed out.\n");
continue;
}
printf("recvfrom failed: %d\n",WSAGetLastError());
break;
}
//成功解碼
if(!decode_resp(recvbuf,iRecv,&from))
statistic++; //記錄成功接收響應資料包的次數
Sleep(1000);
}
//統計執行Ping命令的統計結果
printf("\nPing statistics for %s \n",dest_ip);
printf(" Packets: Sent = %d,Received = %d, Lost = %d (%2.0f%% loss)\n",times,
statistic,(times-statistic), (float)(times-statistic)/times*100);
free(recvbuf);
free(icmp_data);
closesocket(sockRaw);
WSACleanup();
return;
}
//收到響應IP資料包後,對其進行解碼
int decode_resp(char *buf, int bytes,struct sockaddr_in *from)
{
IpHeader *iphdr;
IcmpHeader *icmphdr;
unsigned short iphdrlen;
iphdr = (IpHeader *)buf;
iphdrlen = (iphdr->h_len) * 4 ; //頭部佔幾個節位元組
if (bytes < iphdrlen + ICMP_MIN)
{
printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));
}
//找到ICMP資料包開始的地方
icmphdr = (IcmpHeader*)(buf + iphdrlen);
if (icmphdr->i_type != ICMP_ECHOREPLY)
{
printf("non-echo type %d recvd\n",icmphdr->i_type);
return 1;
}
//是不是發給本程式的資料包
if (icmphdr->i_id != (USHORT)GetCurrentProcessId())
{
printf("someone else''s packet!\n");
return 1;
}
printf("%d bytes from %s:", bytes, inet_ntoa(from->sin_addr));
printf(" icmp_seq = %d. ",icmphdr->i_seq);
printf(" time: %d ms ", GetTickCount()-icmphdr->timestamp); //傳送到接收過程的經歷的時間
printf("\n");
return 0;
}
//計算校驗和
USHORT checksum(USHORT *buffer, int size)
{
unsigned long cksum=0;
while(size >1) {
cksum+=*buffer++;
size -=sizeof(USHORT);
}
if(size) {
cksum += *(UCHAR*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >>16);
return (USHORT)(~cksum);
}
//填充ICMP資料包
void fill_icmp_data(char * icmp_data, int datasize)
{
IcmpHeader *icmp_hdr;
char *datapart;
icmp_hdr = (IcmpHeader *)icmp_data;
icmp_hdr->i_type = ICMP_ECHO;
icmp_hdr->i_code = 0;
icmp_hdr->i_id = (USHORT)GetCurrentProcessId();
icmp_hdr->i_cksum = 0;
icmp_hdr->i_seq = 0;
datapart = icmp_data + sizeof(IcmpHeader);
//資料區隨便填充
memset(datapart,17, datasize - sizeof(IcmpHeader));
}
相關文章
- 邏輯式程式語言極簡實現(使用C#) - 1. 邏輯式程式語言介紹C#
- C語言如何實現泛型程式設計?C語言泛型程式設計
- C語言開發windows程式主要程式結構C語言Windows
- 邏輯式程式語言極簡實現(使用C#) - 4. 程式碼實現(完結)C#
- c語言實用小程式C語言
- 掃雷--C語言實現C語言
- c語言實現階乘C語言
- C語言建立空白Windows視窗程式碼C語言Windows
- 初步瞭解C語言Windows程式設計C語言Windows程式設計
- 聊聊C語言/C++—程式和程式語言C語言C++
- C 語言實現物體檢測:使用 YOLO 模型YOLO模型
- Linux下C語言驗證多程式LinuxC語言
- C語言__LINE__實現原理C語言
- C語言實現檔案加密C語言加密
- c語言實現this指標效果C語言指標
- 高精度加法(C語言實現)C語言
- C語言實現TCP通訊C語言TCP
- Windows7/10實現ICMP(ping命令)Windows
- (嵌入式)Windows與Ubantu下的C語言程式的編譯執行WindowsC語言編譯
- 邏輯式程式語言極簡實現(使用C#) - 3. 執行原理C#
- 排序演算法-C語言實現排序演算法C語言
- 高精度減法(C語言實現)C語言
- C語言實現推箱子游戲C語言
- C語言實現繼承多型C語言繼承多型
- C語言實現桌面貪吃蛇C語言
- 如何使用 JavaScript 實現一門程式語言(1) : 前言JavaScript
- 教你C語言實現通訊錄的詳細程式碼C語言
- Linux下跨語言呼叫C++實踐LinuxC++
- c語言程式實驗————實驗報告十C語言
- c語言程式實驗——實驗報告五C語言
- c語言程式實驗————實驗報告十二C語言
- windows下安裝go語言WindowsGo
- c語言 5.9.2下載C語言
- 使用 R 語言實現簡單的文字識別程式
- C語言/C++程式設計學習:棧的程式碼實現之陣列方案C語言C++程式設計陣列
- Object-C語言Block的實現方式ObjectC語言BloC
- C 語言實現泛型 swap 函式泛型函式
- PID演算法的C語言實現演算法C語言
- C語言如何實現繼承及容器C語言繼承