测试到一个网站的连接时间是网络编程中常常要用到的,一般我们可以采用通过发送icmp包ping的方法,但现在有些服务器禁止了ping,
这样用ping的方法就行不通了。但是我们可以换个角度考虑,直接通过HTTP端口(80)来测试,采用mfc中的CInternetSession,以及
CHttpConnection来实现。
本文针对上述两种方法均给出了实现方式,代码如下:
1.ping方式
Ping.h
#pragma pack(4)
#include <winsock2.h>
#define ICMP_ECHO 8
#define ICMP_ECHOREPLY 0
#define ICMP_MIN 8 //icmp包头部的长度
#define STATUS_FAILED 0xFFFF
#define DEF_PACKET_SIZE 32
#define DEF_PACKET_NUMBER 4
#define MAX_PACKET 1024
#define NO_RESPONSE 1000
#define xmalloc(s) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (s))
#define xfree(p) HeapFree(GetProcessHeap(), 0, (p))
//ip头
typedef struct iphdr
{
unsigned int version:4; //4位版本
unsigned int h_len:4; //4位头部长度
unsigned char tos; //8位服务类型
unsigned short total_len; //16位包总长度(字节数)
unsigned short id; //16位标识
unsigned short flag_frag; //3位标志+13位片偏移
unsigned char ttl; //8位生存时间
unsigned char proto; //8位协议
unsigned short checksum; //16位首部校验和
unsigned int srcIP; //32位源IP地址
unsigned int dstIP; //32位目的IP地址
}IpHeader;
//icmp头
typedef struct icmphdr
{
unsigned char type; //8位类型
unsigned char code; //8位代码
unsigned short chksum; //16位校验和
unsigned short id; //16位标识
unsigned short seq; //16位报文序列号
unsigned long timesstamp; //32位时间戳
}IcmpHeader;
int ping(const char *szDstIP);
void fill_icmp_data(char *icmp_data, int datasize);
unsigned short checksum(unsigned short *buffer, int size);
Ping.cpp
#include "stdafx.h"
#include "Ping.h"
int ping(const char * szDstIP)
{
int nRet = NO_RESPONSE;
SOCKET sockRaw;
struct sockaddr_in inDest;
struct sockaddr_in inFrom;
int nFromLen = sizeof(inFrom);
unsigned long addr = 0;
char *icmp_data;
char *recvbuf;
unsigned short seq_no = 0;
int datasize;
sockRaw = socket(AF_INET,SOCK_RAW, IPPROTO_ICMP);
if (sockRaw == INVALID_SOCKET)
{
OutputDebugString("socket error!/n");
return nRet;
}
int bread;
int timeout = 1000;
bread = setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout, sizeof(timeout));
if(bread == SOCKET_ERROR)
{
fprintf(stderr,"failed to set recv timeout: %d/n",WSAGetLastError());
return nRet;
}
timeout = 1000;
bread = setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout, sizeof(timeout));
if(bread == SOCKET_ERROR)
{
fprintf(stderr,"failed to set send timeout: %d/n",WSAGetLastError());
return nRet;
}
memset(&inDest, 0, sizeof(inDest));
addr = inet_addr(szDstIP);
inDest.sin_addr.s_addr = addr;
inDest.sin_family = AF_INET;
datasize = DEF_PACKET_SIZE;
datasize += sizeof(IcmpHeader);
icmp_data = (char *)xmalloc(MAX_PACKET);
if (icmp_data == NULL)
{
OutputDebugString("icmp_data is NULL/n");
return nRet;
}
recvbuf = (char *)xmalloc(MAX_PACKET);
if (recvbuf == NULL)
{
OutputDebugString("icmp_data is NULL/n");
return nRet;
}
memset(icmp_data, 0, MAX_PACKET);
fill_icmp_data(icmp_data, datasize);
//发送
int nFirstTime = GetTickCount();
((IcmpHeader *)icmp_data)->timesstamp = nFirstTime;
((IcmpHeader *)icmp_data)->seq = seq_no++;
((IcmpHeader *)icmp_data)->chksum = checksum((unsigned short *)icmp_data, datasize);
int nSend;
nSend = sendto(sockRaw, icmp_data, datasize, 0, (const SOCKADDR *)&inDest, sizeof(inDest));
if (nSend == SOCKET_ERROR)
{
return nRet;
}
int nRecv;
nRecv = recvfrom(sockRaw, recvbuf, MAX_PACKET, 0, (SOCKADDR *)&inFrom, &nFromLen);
if (nRecv == SOCKET_ERROR)
{
return nRet;
}
nRet = GetTickCount() - nFirstTime;
return nRet;
}
//填充icmp数据(头+主体)
void fill_icmp_data(char *icmp_data, int datasize)
{
IcmpHeader *icmp_hdr;
char *datapart;
icmp_hdr = (IcmpHeader *)icmp_data;
icmp_hdr->type = ICMP_ECHO;
icmp_hdr->code = 0;
icmp_hdr->id = (unsigned short)GetCurrentProcessId();
icmp_hdr->chksum = 0;
icmp_hdr->seq = 0;
datapart = icmp_data + sizeof(IcmpHeader);
//填充数据全部为A
memset(datapart, 'A', datasize - sizeof(IcmpHeader));
}
unsigned short checksum(unsigned short *buffer, int size)
{
unsigned long chksum = 0;
while (size > 1)
{
chksum += *buffer++;
size -= sizeof(unsigned short);
}
if (size)
{
chksum += *(unsigned char *)buffer;
}
chksum = (chksum >> 16) + (chksum & 0xffff);
chksum += (chksum >> 16);
return (unsigned short)(~chksum);
}
2. HTTP方式
MyPing.h
#include <afxinet.h>
class CMySession : public CInternetSession
{
public:
CMySession(LPCTSTR pszAppName, int nMethod);
virtual void OnStatusCallback(DWORD dwContext, DWORD dwInternetStatus,
LPVOID lpvStatusInfomration, DWORD dwStatusInformationLen);
};
class CMyException : public CException
{
DECLARE_DYNCREATE(CMyException)
public:
CMyException(int nCode = 0);
~CMyException() { }
int m_nErrorCode;
};
int geturl(CString szServerName);
MyPing.cpp
#include "stdafx.h"
#include "MyPing.h"
#pragma warning(push)
#pragma warning(disable: 4100 4667)
#include <iostream>
#pragma warning(pop)
using namespace std;
#include <stdlib.h>
/////////////////////////////////////////////////////////////////////////////
// Globals
LPCTSTR pszURL = NULL;
BOOL bStripMode = FALSE;
BOOL bProgressMode = FALSE;
DWORD dwAccessType = PRE_CONFIG_INTERNET_ACCESS;
DWORD dwHttpRequestFlags =
INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_NO_AUTO_REDIRECT;
const TCHAR szHeaders[] =
_T("Accept: text/*/r/nUser-Agent: MFC_Tear_Sample/r/n");
/////////////////////////////////////////////////////////////////////////////
// CMySession object
CMySession::CMySession(LPCTSTR pszAppName, int nMethod)
: CInternetSession(pszAppName, 1, nMethod)
{
}
void CMySession::OnStatusCallback(DWORD /* dwContext */, DWORD dwInternetStatus,
LPVOID /* lpvStatusInfomration */, DWORD /* dwStatusInformationLen */)
{
if (!bProgressMode)
return;
if (dwInternetStatus == INTERNET_STATUS_CONNECTED_TO_SERVER)
cerr << _T("Connection made!") << endl;
}
IMPLEMENT_DYNCREATE(CMyException, CException)
CMyException::CMyException(int nCode)
: m_nErrorCode(nCode)
{
}
void ThrowMyException(int nCode)
{
CMyException* pEx = new CMyException(nCode);
throw pEx;
}
/////////////////////////////////////////////////////////////////////////////
// Routines
int geturl(CString szServerName)
{
int nFirstTime = GetTickCount();
int nRetCode = 0;
CMySession session(_T("geturl"), dwAccessType);
CHttpConnection* pServer = NULL;
CHttpFile* pFile = NULL;
try
{
// check to see if this is a reasonable URL
CString strServerName(szServerName);
if (strServerName.Find("http://") == -1)
{
strServerName = "http://" + strServerName;
}
CString strObject;
INTERNET_PORT nPort = INTERNET_DEFAULT_HTTP_PORT;
DWORD dwServiceType;
pServer = session.GetHttpConnection(strServerName, nPort);
pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_GET,
strObject, NULL, 1, NULL, NULL, dwHttpRequestFlags);
pFile->AddRequestHeaders(szHeaders);
pFile->SendRequest();
DWORD dwRet;
pFile->QueryInfoStatusCode(dwRet);
// if access was denied, prompt the user for the password
if (dwRet == HTTP_STATUS_DENIED)
{
DWORD dwPrompt;
dwPrompt = pFile->ErrorDlg(NULL, ERROR_INTERNET_INCORRECT_PASSWORD,
FLAGS_ERROR_UI_FLAGS_GENERATE_DATA | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS, NULL);
// if the user cancelled the dialog, bail out
if (dwPrompt != ERROR_INTERNET_FORCE_RETRY)
{
cerr << _T("Access denied: Invalid password/n");
ThrowMyException(1);
}
pFile->SendRequest();
pFile->QueryInfoStatusCode(dwRet);
}
CString strNewLocation;
pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, strNewLocation);
// were we redirected?
// these response status codes come from WININET.H
if (dwRet == HTTP_STATUS_MOVED ||
dwRet == HTTP_STATUS_REDIRECT ||
dwRet == HTTP_STATUS_REDIRECT_METHOD)
{
CString strNewLocation;
pFile->QueryInfo(HTTP_QUERY_RAW_HEADERS_CRLF, strNewLocation);
int nPlace = strNewLocation.Find(_T("Location: "));
if (nPlace == -1)
{
cerr << _T("Error: Site redirects with no new location") << endl;
ThrowMyException(2);
}
strNewLocation = strNewLocation.Mid(nPlace + 10);
nPlace = strNewLocation.Find('/n');
if (nPlace > 0)
strNewLocation = strNewLocation.Left(nPlace);
// close up the redirected site
pFile->Close();
delete pFile;
pServer->Close();
delete pServer;
if (bProgressMode)
{
cerr << _T("Caution: redirected to ");
cerr << (LPCTSTR) strNewLocation << endl;
}
// figure out what the old place was
if (!AfxParseURL(strNewLocation, dwServiceType, strServerName, strObject, nPort))
{
cerr << _T("Error: the redirected URL could not be parsed.") << endl;
ThrowMyException(2);
}
if (dwServiceType != INTERNET_SERVICE_HTTP)
{
cerr << _T("Error: the redirected URL does not reference a HTTP resource.") << endl;
ThrowMyException(2);
}
// try again at the new location
pServer = session.GetHttpConnection(strServerName, nPort);
pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_GET,
strObject, NULL, 1, NULL, NULL, dwHttpRequestFlags);
pFile->AddRequestHeaders(szHeaders);
pFile->SendRequest();
pFile->QueryInfoStatusCode(dwRet);
if (dwRet != HTTP_STATUS_OK)
{
cerr << _T("Error: Got status code ") << dwRet << endl;
ThrowMyException(2);
}
}
cerr << _T("Status Code is ") << dwRet << endl;
pFile->Close();
pServer->Close();
}
catch (CInternetException* pEx)
{
// catch errors from WinINet
TCHAR szErr[1024];
pEx->GetErrorMessage(szErr, 1024);
cerr << _T("Error: (") << pEx->m_dwError << _T(") ");
cerr << szErr << endl;
nRetCode = 2;
pEx->Delete();
}
catch (CMyException* pEx)
{
// catch things wrong with parameters, etc
nRetCode = pEx->m_nErrorCode;
TRACE1("Error: Exiting with CTearException(%d)/n", nRetCode);
pEx->Delete();
}
if (pFile != NULL)
delete pFile;
if (pServer != NULL)
delete pServer;
session.Close();
nRetCode = GetTickCount() - nFirstTime;
return nRetCode;
}
使用方法如下:
void CPageWebSet::OnBnClickedButtonTest()
{
// TODO: 在此添加控件通知处理程序代码
CWaitCursor cursor;
UpdateData(TRUE);
int nIndex = m_strUrl.MakeLower().Find("http://");
if (nIndex != -1)
{
m_strUrl = m_strUrl.Mid(7, m_strUrl.GetLength() - 7);
}
struct hostent *pHost;
pHost = gethostbyname((LPCTSTR)m_strUrl);
char szIP[20];
struct sockaddr_in inIP;
memcpy(&inIP.sin_addr.s_addr, pHost->h_addr_list[0], pHost->h_length);
strcpy(szIP, inet_ntoa(inIP.sin_addr));
int nPintTime = ping(szIP);
if (nPintTime == NO_RESPONSE)
{
m_strUrlInfo.Format("Ping " + m_strUrl + " Time Out/r/n");
nPintTime = geturl(m_strUrl);
CString strTmp;
strTmp.Format("try another way to connect " + m_strUrl + " Time:%d" , nPintTime);
m_strUrlInfo += strTmp;
}
else
{
m_strUrlInfo.Format("Ping " + m_strUrl + " Time:%d" , nPintTime);
}
UpdateData(FALSE);
cursor.Restore();
}
当然,上面的方法肯定存在不足之处,本文也是旨在给出一个初步的方案,希望大家能更好的完善。
有新的想法记得告诉我一声~~,我的qq:27548274,msn:[email protected]