基于ICMP(Ping)的多线程网络通道监视程序(QT)开发
1、 ICMP原理简介
可参考 ICMP(Ping)功能原理及其C++实现简介 。
2、 网络通道监视程序开发
设计原理: 通过PING
功能实现服务器、交换机、网闸等设备的网络检测,判断网络的否可达和TTL
计算 。
具备功能:
- 通过多线程,实现多个网络通道同时检测;
- 支持动态加包。
- 支持设置网络延迟阈值,超过阈值时提示用户。
①、 界面设计
程序采用QT开发,UI界面如下图所示:
中心区域为 QTableWidget
控件。
②、 PING接口封装
IPing.h
:
#pragma once
class IPing
{
public:
IPing();
~IPing();
void SetPackSize(int isize);
void PingHost(const char* ip, int& timems, int& ttl);
private:
int sock;
unsigned int m_iTxID;
unsigned int m_iTxSequeNum;
unsigned int m_iSendPackageSize;
};
void InitWinSockEnv();
void CleanupWinSockEnv();
IPing.cxx
:
#include "IPing.h"
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <iomanip>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#define ICMP_PING_DATA_SIZE 40000 //填充数据长度;
#define ICMP_TYPE_PING_REQUEST 8
#define ICMP_TYPE_PING_REPLY 0
#define ICMP_PING_TIMES 1 //Ping次数
#define MAX_BUFFER_SIZE 40100
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
//ICMP校验和计算
uint16 MakeChecksum(char* icmp_packet, int size)
{
uint16 * sum = (uint16*)icmp_packet;
uint32 checksum = 0;
while (size > 1)
{
checksum += ntohs(*sum++);
size -= sizeof(uint16);
}
if (size)
{
*sum = *((uint8*)sum);
checksum += ((*sum << 8) & 0xFF00);
}
checksum = (checksum >> 16) + (checksum & 0xffff);
checksum += checksum >> 16;
return (uint16)(~checksum);
}
IPing::IPing()
{
sock = 0;
m_iTxID = 0;
m_iTxSequeNum = 0;
m_iSendPackageSize = 32;
SOCKET s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
sock = s;
}
IPing::~IPing()
{
if (sock > 0)
{
closesocket(sock);
}
}
void IPing::SetPackSize(int isize)
{
m_iSendPackageSize = isize;
}
void IPing::PingHost(const char* ip, int& timems, int& ttl)
{
std::string strHost = std::string(ip);
if (strHost.length()< 5)
{
timems = -1;
ttl = -1;
return;
}
struct sockaddr_in pingaddr;
memset((char *)&pingaddr, 0, sizeof(pingaddr));
pingaddr.sin_family = AF_INET;
pingaddr.sin_port = 0;
pingaddr.sin_addr.s_addr = inet_addr(strHost.c_str());
for (int ii = 0; ii < ICMP_PING_TIMES; ++ii)
{
//组帧
uint8 ucCmdBuf[MAX_BUFFER_SIZE] = {
0 };
uint8* pCurr = ucCmdBuf;
*pCurr++ = ICMP_TYPE_PING_REQUEST; //Type
*pCurr++ = 0x00; //Code
*pCurr++ = 0x00; //Checksum
*pCurr++ = 0x00; //Checksum
*pCurr++ = HIBYTE(m_iTxID); //Identifier
*pCurr++ = LOBYTE(m_iTxID); //Identifier
*pCurr++ = HIBYTE(m_iTxSequeNum); //Sequence Number
*pCurr++ = LOBYTE(m_iTxSequeNum); //Sequence Number
auto send_clock = std::chrono::system_clock::now();
DWORD dwSendTime = std::chrono::duration_cast<std::chrono::milliseconds>(send_clock.time_since_epoch()).count();;
*pCurr++ = HIBYTE(HIWORD(dwSendTime)); //Current TickCount
*pCurr++ = LOBYTE(HIWORD(dwSendTime)); //Current TickCount
*pCurr++ = HIBYTE(LOWORD(dwSendTime)); //Current TickCount
*pCurr++ = LOBYTE(LOWORD(dwSendTime)); //Current TickCount
//Data
for (int k = 0; k < m_iSendPackageSize - 4; ++k)
{
*pCurr++ = 0x61 + (k % 20);
}
uint16 usCheckSum = MakeChecksum((char*)ucCmdBuf, pCurr - ucCmdBuf);
ucCmdBuf[2] = HIBYTE(usCheckSum);
ucCmdBuf[3] = LOBYTE(usCheckSum);
int txlen = sendto(sock, (char *)&ucCmdBuf, pCurr - ucCmdBuf, 0, (struct sockaddr *) &pingaddr, sizeof(struct sockaddr_in));
uint8 ucRxBuf[MAX_BUFFER_SIZE] = {
0 };
struct sockaddr_in rxaddr;
memset((char *)&rxaddr, 0, sizeof(rxaddr));
rxaddr.sin_family = AF_INET;
rxaddr.sin_port = 0;
rxaddr.sin_addr.s_addr = inet_addr(strHost.c_str());
int iSize = sizeof(struct sockaddr_in);
do
{
int rxlen = recvfrom(sock, (char *)&ucRxBuf, sizeof(ucRxBuf), 0, (struct sockaddr *)&rxaddr, &iSize);
if (rxaddr.sin_addr.S_un.S_addr == pingaddr.sin_addr.S_un.S_addr)
{
if (rxlen >= 0)
{
//IP帧获取TTL
ttl = ucRxBuf[8];
//跳过IP Header 20个字节
uint8* pIcmpFrame = ucRxBuf + 20;
//Type和Code
uint8 ucType = pIcmpFrame[0];
uint8 ucCode = pIcmpFrame[1];
if (ucCode != 0x00 || ucType != 0x00)
goto GO_ON;
//比对ID和序号
uint16 iRxID = pIcmpFrame[4] * 256 + pIcmpFrame[5];
uint16 iRxSequeNum = pIcmpFrame[6] * 256 + pIcmpFrame[7];
if (iRxID == m_iTxID && iRxSequeNum == m_iTxSequeNum)
{
DWORD dwTimeTransmit = pIcmpFrame[8] * 256 * 256 * 256 + pIcmpFrame[9] * 256 * 256 + pIcmpFrame[10] * 256 + pIcmpFrame[11];
auto now = std::chrono::system_clock::now();
DWORD dwNowTime = std::chrono::duration_cast<std