运维|实现网络连通检测的五种方法

方法一:

  • windows下调用icmp.dll库,实现ping连通检测,缺点是不能跨平台,受限于icmp.dll库:
// windows下调用icmp.dll库实现网络连通检测示例代码#include #include #include #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-Liveunsigned 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.\n"); } else { printf("NOT.\n"); }system("pause"); return 0; }

方法二:
  • 使用原始套接字,模拟实现ping程序以进行网络连通检测,可跨平台,缺点是在linux下使用原始套接字必须拥有超级用户权限:
// 模拟实现ping程序,跨平台检测网络连接#ifdef _WIN32 #include #pragma comment(lib, "WS2_32")struct WindowsSocketLibInit { WindowsSocketLibInit() { WSADATA wsaData; WORD sockVersion = MAKEWORD(2, 2); WSAStartup(sockVersion, &wsaData); } ~WindowsSocketLibInit() { WSACleanup(); } } INITSOCKETGLOBALVARIABLE; #else #include #include #include #include #include #include #include #include #endif#include #include 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::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 #include #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\n"); } else if (1 == ret) { printf("success"); } //printf("ret is %d\n", 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\n"); 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
【运维|实现网络连通检测的五种方法】转载于:https://www.cnblogs.com/yiluyisha/p/10654552.html

    推荐阅读