如何发送一个ping并获取到时间延时

lihongbin33 2008-05-12 12:06:11
我想实现一个功能,ping 某个IP段的IP看哪些有应答并把IP和Time记录下!
...全文
1468 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
qingdou 2008-05-20
  • 打赏
  • 举报
回复
二楼greatws的代码,测试可用
yuntianhai 2008-05-16
  • 打赏
  • 举报
回复
system32下的ICMP.dll的导出函数就可以实现你要求的功能:

//返回值:1表示网络状态正常 ,0表示网络状态异常


// ==================ping的实现部分==================
int doit(int argc, char* argv[])
{

if (argc < 2) {
cerr << "usage: ping <host>" << endl;
return 1;
}

// 装载ICMP.DLL连接库
HINSTANCE hIcmp = LoadLibrary("ICMP.DLL");
if (hIcmp == 0) {
cerr << "Unable to locate ICMP.DLL!" << endl;
return 2;
}

// 查找给定机器的IP地址信息
struct hostent* phe;
if ((phe = gethostbyname(argv[1])) == 0) {
cerr << "Could not find IP address for " << argv[1] << endl;
return 3;
}

// 定义函数三个指针类型
typedef HANDLE (WINAPI* pfnHV)(VOID);
typedef BOOL (WINAPI* pfnBH)(HANDLE);
typedef DWORD (WINAPI* pfnDHDPWPipPDD)(HANDLE, DWORD, LPVOID, WORD,
PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD); // evil, no?
//定义三个指针函数
pfnHV pIcmpCreateFile;
pfnBH pIcmpCloseHandle;
pfnDHDPWPipPDD pIcmpSendEcho;

//从ICMP.DLL中得到函数入口地址
pIcmpCreateFile = (pfnHV)GetProcAddress(hIcmp, "IcmpCreateFile");
pIcmpCloseHandle = (pfnBH)GetProcAddress(hIcmp, "IcmpCloseHandle");
pIcmpSendEcho = (pfnDHDPWPipPDD)GetProcAddress(hIcmp, "IcmpSendEcho");
if ((pIcmpCreateFile == 0) || (pIcmpCloseHandle == 0) ||
(pIcmpSendEcho == 0)) {
cerr << "Failed to get proc addr for function." << endl;
return 4;
}

// 打开ping服务
HANDLE hIP = pIcmpCreateFile();
if (hIP == INVALID_HANDLE_VALUE) {
cerr << "Unable to open ping service." << endl;
return 5;
}

// 构造ping数据包
char acPingBuffer[64];
memset(acPingBuffer, '\xAA', sizeof(acPingBuffer));
PIP_ECHO_REPLY pIpe = (PIP_ECHO_REPLY)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT,
sizeof(IP_ECHO_REPLY) + sizeof(acPingBuffer));
if (pIpe == 0) {
cerr << "Failed to allocate global ping packet buffer." << endl;
return 6;
}
pIpe->Data = acPingBuffer;
pIpe->DataSize = sizeof(acPingBuffer);

// 发送ping数据包
DWORD dwStatus = pIcmpSendEcho(hIP, *((DWORD*)phe->h_addr_list[0]),
acPingBuffer, sizeof(acPingBuffer), NULL, pIpe,
sizeof(IP_ECHO_REPLY) + sizeof(acPingBuffer), 5000);
if (dwStatus != 0) {
cerr << "Addr: " <<
int(LOBYTE(LOWORD(pIpe->Address))) << "." <<
int(HIBYTE(LOWORD(pIpe->Address))) << "." <<
int(LOBYTE(HIWORD(pIpe->Address))) << "." <<
int(HIBYTE(HIWORD(pIpe->Address))) << ", " <<
"RTT: " << int(pIpe->RoundTripTime) << "ms, " <<
"TTL: " << int(pIpe->Options.Ttl) << endl;
}
else {
cerr << "Error obtaining info from ping packet." << endl;
cout<<dwStatus<<" "<<GetLastError()<<endl;
}

// 关闭,回收资源
GlobalFree(pIpe);
FreeLibrary(hIcmp);
return 0;
}
greatws 2008-05-15
  • 打赏
  • 举报
回复
呵呵,人人都有搞错的时候,我也经常搞错。

以前我也不知道原始套接字对SP2的限制,受教了,照这么说,原始套接字算废了。想欺骗要用winpcap之类的底层库了

zdleek 2008-05-15
  • 打赏
  • 举报
回复
mark
vocanicy 2008-05-14
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 lihongbin33 的回复:]
to vocanicy
你的代码在vc 6.0下运行时要出错,正在找错误....
[/Quote]

楼上说的没错,XP以上版本的SDK里有Icmpxxx函数
我装的MSDN版本太久了居然找不到

不过这个函数只能在XP以后系统使用,2000不支持,不过现在应该很少人用2000了。后面帖了一个例子!

谢谢aaronwang81提醒!

另外也给greatws道个歉,XP SP2里的Raw Socket只限制了TCP报文的发送,对UDP报文只限制源地址。
因此,你的代码是可以用的。我没有仔细测试过,就否定你的方法,真的不好意思!


Limitations on Raw Sockets
On Windows XP Service Pack 2 (SP2) and Windows Vista, the ability to send traffic over raw sockets has been restricted in several ways:

TCP data cannot be sent over raw sockets. (无法通过原始套接字发送TCP报文)

UDP datagrams with an invalid source address cannot be sent over raw sockets. The IP source address for any outgoing UDP datagram must exist on a network interface or the datagram is dropped. This change was made to limit the ability of malicious code to create distributed denial-of-service attacks and limits the ability to send spoofed packets (TCP/IP packets with a forged source IP address). (只要源地址是合法地址,UDP报文是可以发送的)

A call to the bind function with a raw socket is not allowed.
These above restrictions to not apply to Windows Server 2003 and Windows Server 2008 or to versions of the operating system earlier than Windows XP SP2.




HANDLE hIcmpFile;
if ((hIcmpFile = IcmpCreateFile()) == INVALID_HANDLE_VALUE)
{
printf("\tUnable to open file.\n");
return;
}
else
printf("\tFile created.\n");

char *SendData = "Data Buffer";
LPVOID ReplyBuffer;

ReplyBuffer = (VOID*) malloc(sizeof(ICMP_ECHO_REPLY) + sizeof(SendData));
if ((dwRetVal = IcmpSendEcho(hIcmpFile,
inet_addr("123.456.789.0"),
SendData, sizeof(SendData),
NULL, ReplyBuffer,
sizeof(ReplyBuffer) + sizeof(ICMP_ECHO_REPLY),
1000)) != 0) {
printf("\tReceived %ld messages.\n", dwRetVal);
printf("\tMessage: %s\n", ReplyBuffer);
}
else {
printf("\tCall to IcmpSendEcho() failed.\n");
printf("\tError: %ld\n", GetLastError());
}
龙凤呈祥焱 2008-05-14
  • 打赏
  • 举报
回复
哎。。。
API里面有Icmp开头的函数。可以执行ping功能。非要自己写些SP2用不了的代码。不郁闷么。
lihongbin33 2008-05-14
  • 打赏
  • 举报
回复
to vocanicy
你的代码在vc 6.0下运行时要出错,正在找错误....
lihongbin33 2008-05-14
  • 打赏
  • 举报
回复
to greatws
你的代码在 XP SP2下能用,但是第2次ping 以后就有问题,TIME特别大,没仔细看,估计是数据包构造有问题!
ping www.sina.com试下~
vocanicy 2008-05-12
  • 打赏
  • 举报
回复
提醒一下楼主:以上例子的代码在XP SP2无法使用,因为在SP2里限制RAW SOCKET发送报文。

简单的实现就用管道连接执行ping.exe,然后解析返回字符串。
如果一定要自己构造报文来发送,也可以用第三方的winpcap库来发送。
greatws 2008-05-12
  • 打赏
  • 举报
回复
给你个ping的程序,看看就清楚了,注释很清楚



#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")

typedef struct icmp_hdr
{
unsigned char icmp_type; // 消息类型
unsigned char icmp_code; // 代码
unsigned short icmp_checksum; // 校验和
// 下面是回显头
unsigned short icmp_id; // 用来惟一标识此请求的ID号,通常设置为进程ID
unsigned short icmp_sequence; // 序列号
unsigned long icmp_timestamp; // 时间戳
} ICMP_HDR, *PICMP_HDR;

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;

USHORT checksum(USHORT* buff, int size);

BOOL SetTimeout(SOCKET s, int nTime, BOOL bRecv = TRUE);

int main()
{
// 目的IP地址,即要Ping的IP地址
char szDestIp[] = "127.0.0.1"; // 127.0.0.1

WSADATA wsaData;
WORD sockVersion = MAKEWORD(2,2);
if(::WSAStartup(sockVersion, &wsaData) != 0)
{
return -1;
}

// 创建原始套节字
SOCKET sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

// 设置接收超时
SetTimeout(sRaw, 1000, TRUE);

// 设置目的地址
SOCKADDR_IN dest;
dest.sin_family = AF_INET;
dest.sin_port = htons(0);
dest.sin_addr.S_un.S_addr = inet_addr(szDestIp);

// 创建ICMP封包
char buff[sizeof(ICMP_HDR) + 32];
ICMP_HDR* pIcmp = (ICMP_HDR*)buff;
// 填写ICMP封包数据
pIcmp->icmp_type = 8; // 请求一个ICMP回显
pIcmp->icmp_code = 0;
pIcmp->icmp_id = (USHORT)::GetCurrentProcessId();
pIcmp->icmp_checksum = 0;
pIcmp->icmp_sequence = 0;
// 填充数据部分,可以为任意
memset(&buff[sizeof(ICMP_HDR)], 'E', 32);

// 开始发送和接收ICMP封包
USHORT nSeq = 0;
char recvBuf[1024];
SOCKADDR_IN from;
int nLen = sizeof(from);
while(TRUE)
{
static int nCount = 0;
int nRet;
if(nCount++ == 4)
break;
pIcmp->icmp_checksum = 0;
pIcmp->icmp_timestamp = ::GetTickCount();
pIcmp->icmp_sequence = nSeq++;
pIcmp->icmp_checksum = checksum((USHORT*)buff, sizeof(ICMP_HDR) + 32);
nRet = ::sendto(sRaw, buff, sizeof(ICMP_HDR) + 32, 0, (SOCKADDR *)&dest, sizeof(dest));
if(nRet == SOCKET_ERROR)
{
printf(" sendto() failed: %d \n", ::WSAGetLastError());
return -1;
}
nRet = ::recvfrom(sRaw, recvBuf, 1024, 0, (sockaddr*)&from, &nLen);
if(nRet == SOCKET_ERROR)
{
if(::WSAGetLastError() == WSAETIMEDOUT)
{
printf("Request timed out\n");
continue;
}
printf(" recvfrom() failed: %d\n", ::WSAGetLastError());
return -1;
}

// 下面开始解析接收到的ICMP封包
int nTick = ::GetTickCount();
if(nRet < sizeof(IPHeader) + sizeof(ICMP_HDR))
{
printf(" Too few bytes from %s \n", ::inet_ntoa(from.sin_addr));
}
// 接收到的数据中包含IP头,IP头大小为20个字节,所以加20得到ICMP头
ICMP_HDR* pRecvIcmp = (ICMP_HDR*)(recvBuf + 20); // (ICMP_HDR*)(recvBuf + sizeof(IPHeader));
if(pRecvIcmp->icmp_type != 0) // 回显
{
printf(" nonecho type %d recvd \n", pRecvIcmp->icmp_type);
return -1;
}

if(pRecvIcmp->icmp_id != ::GetCurrentProcessId())
{
printf(" someone else's packet! \n");
return -1;
}

printf(" %d bytes from %s:", nRet, inet_ntoa(from.sin_addr));
printf(" icmp_seq = %d. ", pRecvIcmp->icmp_sequence);
printf(" time: %d ms", nTick - pRecvIcmp->icmp_timestamp);
printf(" \n");

::Sleep(1000);
}

return 0;
}

USHORT checksum(USHORT* buff, int size)
{
unsigned long cksum = 0;
while(size>1)
{
cksum += *buff++;
size -= sizeof(USHORT);
}
// 是奇数
if(size)
{
cksum += *(UCHAR*)buff;
}
// 将32位的chsum高16位和低16位相加,然后取反
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;
}




bigpeon 2008-05-12
  • 打赏
  • 举报
回复
学习了
greatws 2008-05-12
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 vocanicy 的回复:]
提醒一下楼主:以上例子的代码在XP SP2无法使用,因为在SP2里限制RAW SOCKET发送报文。

简单的实现就用管道连接执行ping.exe,然后解析返回字符串。
如果一定要自己构造报文来发送,也可以用第三方的winpcap库来发送。
[/Quote]

我就是SP2,没有你说的问题呀
vocanicy 2008-05-12
  • 打赏
  • 举报
回复
上面的例子,我限制只ping一次,3个时间相等(最大、最小、平均),所以时间就取了平均时间
你可以根据需要自己修改一下
vocanicy 2008-05-12
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 lihongbin33 的回复:]
to ls:
另开启一个可执行程序ping.exe后,自己的程序如何获取其返回字符串?
[/Quote]

我写了一段代码,你要的功能已经实现了

DWORD dwTime;
if(Ping("www.baidu.com", dwTime))
{
...
}


// 以太工作室 东东
// 函数:Ping
// 参数:
// szTarget目标地址(可以是域名、IP地址)
// dwTime返回时间;如果是超时,等于INFINITE
BOOL Ping(LPCTSTR szTarget, DWORD &dwTime)
{
BOOL bSuccess = FALSE;
dwTime = INFINITE;

if(szTarget == NULL)
{
TRACE0("Target Is NULL\n");
return FALSE;
}

TCHAR szCmdLine[80];
if(_sntprintf(szCmdLine, sizeof(szCmdLine) / sizeof(TCHAR),
_T("ping.exe -n 1 %s"), szTarget) == sizeof(szCmdLine) / sizeof(TCHAR))
{
TRACE0("Target Is Too Long\n");
return FALSE;
}

HANDLE hWritePipe = NULL;
HANDLE hReadPipe = NULL;
HANDLE hWriteShell = NULL;
HANDLE hReadShell = NULL;

SECURITY_ATTRIBUTES sa;
memset(&sa, 0, sizeof(sa));
sa.nLength = sizeof(sa);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;

if(CreatePipe(&hReadPipe, &hReadShell, &sa, 0)
&& CreatePipe(&hWriteShell, &hWritePipe, &sa, 0))
{
STARTUPINFO si;
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdInput = hWriteShell;
si.hStdOutput = hReadShell;
si.hStdError = hReadShell;
si.wShowWindow = SW_HIDE;

PROCESS_INFORMATION pi;
memset(&pi, 0, sizeof(pi));

int nMin = -1, nMax = -1, nAvg = -1;
if(CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
if(WaitForSingleObject(pi.hProcess, 10000) == WAIT_OBJECT_0)
{
TCHAR szBuffer[1024];
DWORD dwBytes;
if(ReadFile(hReadPipe, szBuffer, sizeof(szBuffer), &dwBytes, NULL))
{
szBuffer[dwBytes] = '\0';

LPTSTR lpszTime = NULL;
lpszTime = _tcsstr(szBuffer, _T("Request timed out"));
if(lpszTime == NULL)
{
lpszTime = _tcsstr(szBuffer, _T("Minimum"));
if(lpszTime != NULL)
{
if(_stscanf(lpszTime, _T("Minimum = %dms, Maximum = %dms, Average = %dms"),
&nMin, &nMax, &nAvg) == 3)
{
TRACE3("%d, %d, %d\n", nMin, nMax, nAvg);

dwTime = nAvg;
bSuccess = TRUE;
}
}
else
{
TRACE0("PING FORMAT is Error\n");
}
}
else
{
TRACE0("PING is Time Out\n");

bSuccess = TRUE;
}

}
}
else
{
TRACE1("Process(%d) is Time Out\n", pi.dwProcessId);
TerminateProcess(pi.hProcess, 0);
}

CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);

TRACE3(_T("Minimum = %dms, Maximum = %dms, Average = %dms\n"), nMin, nMax, nAvg);
}
}

if(hWritePipe != NULL)
CloseHandle(hWritePipe);
if(hReadPipe != NULL)
CloseHandle(hReadPipe);
if(hWriteShell != NULL)
CloseHandle(hWriteShell);
if(hReadShell != NULL)
CloseHandle(hReadShell);

return bSuccess;
}
scq2099yt 2008-05-12
  • 打赏
  • 举报
回复
up
lihongbin33 2008-05-12
  • 打赏
  • 举报
回复
to ls:
另开启一个可执行程序ping.exe后,自己的程序如何获取其返回字符串?

18,358

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧