实现网络连通检测的五种方法
Posted yiluyisha
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现网络连通检测的五种方法相关的知识,希望对你有一定的参考价值。
方法一:
- windows下调用icmp.dll库,实现ping连通检测,缺点是不能跨平台,受限于icmp.dll库:
// windows下调用icmp.dll库实现网络连通检测示例代码
#include <Winsock2.h>
#include <iphlpapi.h>
#include <stdio.h>
#pragma comment(lib,"Iphlpapi.lib")
#pragma comment(lib,"Ws2_32.lib")
typedef HANDLE (WINAPI* ICMPCREATEFILE)(VOID);
typedef BOOL (WINAPI* ICMPCLOSEHANDLE)(HANDLE);
typedef DWORD (WINAPI* ICMPSENDECHO)(HANDLE, DWORD, LPVOID, WORD,PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD);
// 定义三个指针函数
ICMPCREATEFILE pIcmpCreateFile;
ICMPCLOSEHANDLE pIcmpCloseHandle;
ICMPSENDECHO pIcmpSendEcho;
// 函数功能:初始化ICMP函数:
BOOL InitIcmp()
{
HINSTANCE hIcmp = LoadLibrary(TEXT("ICMP.DLL")); // 需自行下载icmp.dll动态库
if(hIcmp==NULL)
{
return false;
}
pIcmpCreateFile = (ICMPCREATEFILE)GetProcAddress(hIcmp,"IcmpCreateFile");
pIcmpCloseHandle = (ICMPCLOSEHANDLE)GetProcAddress(hIcmp,"IcmpCloseHandle");
pIcmpSendEcho = (ICMPSENDECHO)GetProcAddress(hIcmp,"IcmpSendEcho");
if ((pIcmpCreateFile == NULL)||(pIcmpCloseHandle == NULL)||(pIcmpSendEcho == NULL))
return false;
return true;
}
// 函数功能:判断是否能ping通IP
// 函数参数:IP地址或域名
BOOL ICMPPing(char* host)
{
DWORD timeOut=1000; // 设置超时
ULONG hAddr=inet_addr(host); // 如果是IP地址就直接转换
if(hAddr==INADDR_NONE)
{
hostent* hp=gethostbyname(host); // 如果是域名就用DNS解析出IP地址
if(hp)
memcpy(&hAddr,hp->h_addr_list,hp->h_length); // IP地址
else
{
return false;
}
}
HANDLE hIp=pIcmpCreateFile();
IP_OPTION_INFORMATION ipoi;
memset(&ipoi,0,sizeof(IP_OPTION_INFORMATION));
ipoi.Ttl =128; //Time-To-Live
unsigned char pSend[36]; // 发送包
memset(pSend,'E',32);
int repSize=sizeof(ICMP_ECHO_REPLY)+32;
unsigned char pReply[100]; // 接收包
ICMP_ECHO_REPLY* pEchoReply=(ICMP_ECHO_REPLY*)pReply;
DWORD nPackets=pIcmpSendEcho(hIp,hAddr,pSend,32,&ipoi,pReply,repSize,timeOut); // 发送ICMP数据报文
if(pEchoReply->Status!=0) // 超时,可能是主机禁用了ICMP 或者目标主机不存在
{
pIcmpCloseHandle(hIp);
return false;
}
pIcmpCloseHandle(hIp);
return true;
}
int main()
{
InitIcmp();
if (true == ICMPPing("127.0.0.1"))
{
printf("OK.
");
}
else
{
printf("NOT.
");
}
system("pause");
return 0;
}
方法二:
- 使用原始套接字,模拟实现ping程序以进行网络连通检测,可跨平台,缺点是在linux下使用原始套接字必须拥有超级用户权限:
// 模拟实现ping程序,跨平台检测网络连接
#ifdef _WIN32
#include <WinSock2.h>
#pragma comment(lib, "WS2_32")
struct WindowsSocketLibInit
{
WindowsSocketLibInit()
{
WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 2);
WSAStartup(sockVersion, &wsaData);
}
~WindowsSocketLibInit()
{
WSACleanup();
}
} INITSOCKETGLOBALVARIABLE;
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <ctime>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#endif
#include <string>
#include <limits>
unsigned short getChecksum(unsigned short *buff, unsigned size);
bool ping(std::string ip)
{
static unsigned INDEX = 0;
const unsigned IP_HEADER_LENGTH = 20;
const unsigned FILL_LENGTH = 32;
struct IcmpHdr
{
unsigned char icmpType;
unsigned char icmpCode;
unsigned short icmpChecksum;
unsigned short icmpId;
unsigned short icmpSequence;
};
int socketFd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
int timeoutTick = 200;
setsockopt(socketFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeoutTick, sizeof(timeoutTick));
sockaddr_in des = {AF_INET, htons(0)};
des.sin_addr.s_addr = inet_addr(ip.c_str());
char buff[sizeof(IcmpHdr) + FILL_LENGTH] = {0};
IcmpHdr *pIcmpHdr = (IcmpHdr *)(buff);
unsigned short id = std::rand() % (std::numeric_limits<unsigned short>::max)();
pIcmpHdr->icmpType = 8;
std::cout << "---" << pIcmpHdr->icmpType << std::endl;
pIcmpHdr->icmpCode = 0;
pIcmpHdr->icmpId = id;
pIcmpHdr->icmpSequence = INDEX++;
std::memcpy(&buff[sizeof(IcmpHdr)], "TestTest", sizeof("TestTest"));
pIcmpHdr->icmpChecksum = getChecksum((unsigned short *)buff, sizeof(buff));
if (-1 == sendto(socketFd, buff, sizeof(buff), 0, (sockaddr *)&des, sizeof(des)))
{
return false;
}
char recv[1 << 10];
int ret = recvfrom(socketFd, recv, sizeof(recv), 0, NULL, NULL);
if (-1 == ret || ret < IP_HEADER_LENGTH + sizeof(IcmpHdr))
{
return false;
}
IcmpHdr *pRecv = (IcmpHdr *)(recv + IP_HEADER_LENGTH);
return !(pRecv->icmpType != 0 || pRecv->icmpId != id);
}
unsigned short getChecksum(unsigned short *buff, unsigned size)
{
unsigned long ret = 0;
for (unsigned i = 0; i < size; i += sizeof(unsigned short))
{
ret += *buff++;
}
if (size & 1)
{
ret += *(unsigned char *)buff;
}
ret = (ret >> 16) + (ret & 0xFFFF);
ret += ret >> 16;
return (unsigned short)~ret;
}
方法三:
- 使用非阻塞connect函数和select定时相结合来检测网络连通,可跨平台,以下为windows下实现代码:
// 使用非阻塞connect和select定时检测解决connect失败时阻塞时间过长的问题
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
// 网络初始化
WORD wVersionRequested;
WSADATA wsaData;
wVersionRequested = MAKEWORD(2, 2);
WSAStartup( wVersionRequested, &wsaData );
// 创建客户端socket(默认为是阻塞socket)
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);
// 设置为非阻塞的socket
int iMode = 1;
ioctlsocket(sockClient, FIONBIO, (u_long FAR*)&iMode);
// 定义服务端
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.26.67");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(31800);
// 超时时间
struct timeval tm;
tm.tv_sec = 0;
tm.tv_usec = 5000;
int ret = -1;
// 尝试去连接服务端
if (-1 != connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR)))
{
ret = 1; // 连接成功
}
else
{
fd_set set;
FD_ZERO(&set);
FD_SET(sockClient, &set);
if (select(-1, NULL, &set, NULL, &tm) <= 0)
{
ret = -1; // 有错误(select错误或者超时)
}
else
{
int error = -1;
int optLen = sizeof(int);
getsockopt(sockClient, SOL_SOCKET, SO_ERROR, (char*)&error, &optLen);
// 之所以下面的程序不写成三目运算符的形式, 是为了更直观, 便于注释
if (0 != error)
{
ret = -1; // 有错误
}
else
{
ret = 1; // 无错误
}
}
}
// 设回为阻塞socket
iMode = 0;
ioctlsocket(sockClient, FIONBIO, (u_long FAR*)&iMode); //设置为阻塞模式
// connect状态
if (-1 == ret) {
printf("error
");
} else if (1 == ret) {
printf("success");
}
//printf("ret is %d
", ret);
// 发送数据到服务端测试以下
if(1 == ret)
{
send(sockClient, "hello world", strlen("hello world") + 1, 0);
}
// 释放网络连接
closesocket(sockClient);
WSACleanup();
getchar();
return 0;
}
方法四:
- 使用setsockopt函数设定socket连接、接收和发送的响应时间,可以通过connect之前设定SO_SNDTIMO来达到控制连接超时的目的:
// 自行添加相应头文件
int main(int argc, char *argv[])
{
int fd;
struct sockaddr_in addr;
struct timeval timeo = {3, 0};
socklen_t len = sizeof(timeo);
fd = socket(AF_INET, SOCK_STREAM, 0);
if (argc == 4)
timeo.tv_sec = atoi(argv[3]);
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, len);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
if (errno == EINPROGRESS) {
fprintf(stderr, "timeout/n");
return -1;
}
perror("connect");
return 0;
}
printf("connected/n");
return 0;
}
方法五:
- linux下直接调用执行ping子程序以实现检测网络连通功能:
// 自行添加相应代码文件
int go_ping(char *svrip)
{
int i = 0;
while(i < 3)
{
pid_t pid;
if ((pid = vfork()) < 0)
{
printf("vfork error");
exit(1);
}
else if (pid == 0)
{
if ( execlp("ping", "ping","-c 1",svrip, (char*)0) < 0)
{
printf("execlp error
");
exit(1);
}
}
int stat;
waitpid(pid, &stat, 0);
if (stat == 0)
{
return 0;
}
sleep(3);
i++;
}
return -1;
}
参考资料:
- http://www.imooc.com/wenda/detail/443658
- https://blog.csdn.net/qq_30650153/article/details/86244309
- https://www.jianshu.com/p/95515d39897c
- http://bbs.chinaunix.net/thread-2076080-1-1.html
- https://blog.csdn.net/hj605635529/article/details/74157305
以上是关于实现网络连通检测的五种方法的主要内容,如果未能解决你的问题,请参考以下文章