前两天写了个小程序,现在给大家分享一下.这个是用c/c++加上原始套接字写的一个ping命令,ping命令的原理想必我不用多说了,了解ICMP协议的朋友们都知道的..
该程序分几个文件:
初始化socket的类:InitSocket.h

/**//* winsock应用程序必须加载Winsock Dll
* 加载该Dll的函数就是::WSAStartup()
* 使用结束后还得释放Dll 用::WSACleanup()函数可以实现
*/
#ifndef _INITSOCKET_H_
#define _INITSOCKET_H_
#include <winsock2.h>
#include <Mswsock.h>
#pragma comment(lib, "WS2_32")
class CInitSocket

...{
public:
CInitSocket(BYTE minorVer = 2, BYTE majorVer = 2)

...{
WSADATA wsaData;
WORD sockVersion = MAKEWORD(minorVer, majorVer);
if (::WSAStartup(sockVersion, &wsaData) != 0)

...{
return;
}
}
~CInitSocket()

...{
::WSACleanup();
}
};
#endif //_INITSOCKET_H_
子函数头文件HdFuns.h
#ifndef _HDFUNS_H_
#define _HDFUNS_H_
#include "InitSocket.h"
USHORT CheckSum(USHORT *buffer, int size);
BOOL SetTimeout(SOCKET s, int nTime, BOOL bRecv);
#endif //_HDFUNS_H_
子函数实现文件:HdFuns.cpp
#include "HdFuns.h"
#include "Ws2tcpip.h"
#include <winsock2.h>
//校验和计算
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);
}
设置套接字超时时间
BOOL SetTimeout(SOCKET s, int nTime, BOOL bRecv)

...{
int ret = ::setsockopt(s, SOL_SOCKET, bRecv ? SO_RCVTIMEO : SO_SNDTIMEO, (char *)&nTime, sizeof(nTime));
return ret != SOCKET_ERROR;
}
主函数:main.cpp
#include <iostream>
#include "HdFuns.h"
#include <windows.h>
#include <stdio.h>
using namespace std;
CInitSocket initSocket;
#define PROTO_ICMP 1
#define PROTO_IGMP 2
#define PROTO_TCP 6
#define PROTO_UDP 17
typedef struct _IPHeader // 20字节的IP头

...{
UCHAR iphVerLen; // 版本号和头长度(各占4位)
UCHAR ipTOS; // 服务类型
USHORT ipLength; // 封包总长度,即整个IP报的长度
USHORT ipID; // 封包标识,惟一标识发送的每一个数据报
USHORT ipFlags; // 标志
UCHAR ipTTL; // 生存时间,就是TTL
UCHAR ipProtocol; // 协议,可能是TCP、UDP、ICMP等
USHORT ipChecksum; // 校验和
ULONG ipSource; // 源IP地址
ULONG ipDestination; // 目标IP地址
} IPHeader, *PIPHeader;
typedef struct icmp_hdr

...{
unsigned char icmp_type;
unsigned char icmp_code;
unsigned short icmp_checksum;
unsigned short icmp_id;
unsigned short icmp_sequence;
int icmp_timestamp;
}ICMP_HDR, *PICMP_HDR;
int main(void)

...{
char szDest[50] ;//目标IP
while(true)

...{
cout<<"IP: ";
cin>>szDest;
SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);//原始套接字创建
SetTimeout(sRaw, 1000, TRUE);//设置超时
//目标地址
SOCKADDR_IN dest;
dest.sin_port = ::htons(0);
dest.sin_addr.S_un.S_addr = ::inet_addr(szDest);
dest.sin_family = AF_INET;
char buffer[sizeof(ICMP_HDR) + 1024];
ICMP_HDR *pIcmp = (ICMP_HDR *)buffer;
pIcmp->icmp_type = 8;
pIcmp->icmp_code = 0;
pIcmp->icmp_id = ::GetCurrentProcessId();
pIcmp->icmp_sequence = 0;
pIcmp->icmp_checksum = 0;
//填充数据域
::memset(&buffer[sizeof(ICMP_HDR)], 'E', 32);
USHORT nSeq = 0;
char recvBuf[1024];
SOCKADDR_IN from;
int nLen = sizeof(from);
for (int i = 0; i < 4; i++)

...{
int nRet;
pIcmp->icmp_checksum = 0;
pIcmp->icmp_timestamp = ::GetTickCount();
pIcmp->icmp_sequence = nSeq++;
pIcmp->icmp_checksum = CheckSum((USHORT *)buffer, sizeof(ICMP_HDR) + 32);
//发送请求回显的ICMP报文
nRet = ::sendto(sRaw, buffer, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR *)&dest, sizeof(dest));
if (nRet == SOCKET_ERROR)

...{
cout<<"SendTo Failed: "<<::WSAGetLastError()<<endl;
continue;
}
//接受回显报文
nRet = ::recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr *)&from, &nLen);
if (nRet == SOCKET_ERROR)

...{
cout<<"Request timed out!!"<<endl;
continue;
}
int nTick = ::GetTickCount();
if (nRet < sizeof(IPHeader) + sizeof(ICMP_HDR))

...{
cout<<"too few bytes!"<<endl;
}
PICMP_HDR pRecvIcmp = (PICMP_HDR)(recvBuf + sizeof(IPHeader));
PIPHeader pRecvIP = (PIPHeader)(recvBuf);
if (pRecvIcmp->icmp_type != 0)

...{
cout<<"nonecho type "<<pRecvIcmp->icmp_type<<"recved"<<endl;
continue;
}
if (pRecvIcmp->icmp_id != ::GetCurrentProcessId())

...{
cout<<"someone else's packet!"<<endl;
continue;
}
//报文分析
cout<<nRet<<"bytes from "<<inet_ntoa(from.sin_addr)<<" ";
cout<<"icmp_seq = "<<pRecvIcmp->icmp_sequence<<" ";
cout<<"time = "<<(nTick - pRecvIcmp->icmp_timestamp)<<"ms"<<endl;
::Sleep(1000);
}
}
return 0;
}
以下是执行结果: