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

方法一:

  • 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.\n");
    }
    else
    {
        printf("NOT.\n");
    }

    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\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;
}

参考资料:

原文地址:https://www.cnblogs.com/yiluyisha/p/10654552.html

时间: 2024-11-07 00:10:40

实现网络连通检测的五种方法的相关文章

网络超时检测的三种方法

作者:于老师,华清远见嵌入式学院讲师. 网络通信中,很多操作会使得进程阻塞,这时我们要设定时间,到时间后强制返回,避免进程在没有数据的情况下无限阻塞 这里我们总结一下网络超时检测的三种方法: 通过setsockopt设置套接字属性SO_RCVTIMEO struct timeval t = {5, 0}           if  (setsockopt(listenfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)) == -1) {             

Windows查看电脑上安装的.Net Framework版本的五种方法(转)

1.查看安装文件判断Framwork版本号 打开资源管理器,比如我的电脑,再地址栏输入%systemroot%\Microsoft.NET\Framework后单击“转到”或者按回车. 在新文件夹中查看v字开头的下级文件夹名,最大的数字就是当前计算机上的.Net FrameWork版本号.如文件夹名最大为v4.0.30319,那么本机上安装的.Net Framework版本就是4.0: 2.JavaScript脚本判断.Net Framework版本 在网页浏览器的地址栏输入下面的命令后单击“转

通过CRM系统提高销售效率的五种方法

随着移动互联网时代的不断发展,越来越多的企业利用网络的移动性,来帮助企业节省办公时间.提高工作效率.而罗浮云计算CRM系统也是顺应移动互联网的发展,是现代移动终端技术.销售人员利用它可以摆脱时间和场所局限,随时随地与企业业务平台沟通,不仅可以销售人员与客户联系更加紧密,还能对提高业绩有着积极的作用. 销售人员利用罗浮云计算CRM系统可以摆脱时间和场所局限,随时随地与企业业务平台沟通,不仅可以销售人员与客户联系更加紧密,还能对提高业绩有着积极的作用. 往下具体看利用罗浮云计算CRM系统提高销售效率

HTML 页面跳转的五种方法

H方法TML 页面跳转的五种方法 下面列了五个例子来详细说明,这几个例子的主要功能是:在5秒后,自动跳转到同目录下的hello.html(根据自己需要自行修改)文件.1) html的实现 <head><!-- 以下方式只是刷新不跳转到其他页面 --><meta http-equiv="refresh" content="10"><!-- 以下方式定时转到其他页面 --><meta http-equiv="

linux关机重启五种方法(sysrq shutdown halt init ipmi)

linux关机重启五种方法(sysrq shutdown halt init) 当前版本 cat /etc/debian_version 5.0.1 一.linux关机命令(前三种) 1.shutdown -h now(-h:halt after shutdown.) halt init 0 2.linux重启命令 shutdown -r(-r:reboot after shutdown.) reboot init 6 二.特殊情况重启和关机(能ping通 上述命令无法使用情况下 也就是系统挂起

js去掉字符串前后空格的五种方法

第一种:循环检查替换[javascript]//供使用者调用  function trim(s){  return trimRight(trimLeft(s));  }  //去掉左边的空白  function trimLeft(s){  if(s == null) {  return "";  }  var whitespace = new String(" \t\n\r");  var str = new String(s);  if (whitespace.i

App一炮而红的五种方法,只要达到必火

随着移动互联网金融的火爆,以及社交产品的火爆,现在这两个方面的App也越来越多,App多了,大家都想着怎么做用户,而传统的方法又比较慢,所以说大家都是来问我,有没有让自家产品一炮而红的方法,或者说捷径.于是,中午吃米线的间隙就根据自己的所见所闻思考了下面的五个方法,给大家参考.而且,只要达到了下面的四种方法,你的App就能一炮而红. 第一种:烧钱型 这种方法就是投资方或者创业者非常有钱,到底有多有钱,至少烧个10亿,20亿不成问题.20亿买1亿用户,60亿买2亿用户,手机用户也就6亿,买个一两亿

五种方法实现Linux批量重命名文件

五种方法实现Linux批量重命名文件 Linux批量重命名文件是指对某些特定的文件统一进行重新命名,以改变原来一批文件的名称,这里介绍五种方法来实现. AD: Linux批量重命名文件会涉及到改变一个字母.改变一些相连字母.改变某些位置的字母.在最前面加上某些字母.或者改变字母的大小写.完成这里五个方法基本上就会解决了Linux批量重命名的工作. 1.我想把它们的名字的第一个1个字母变为"q",其它的不变 [[email protected] mailqueue]# for i in

Java中线程等待(同步)的五种方法

在面试时,经常会有面试官问道,一个主线程有多个子线程,如何能使子线程的业务执行完成之后,再执行主线程业务逻辑.对于这个问题,本人能够想到的有五种方法,详细请移步源码 1.使用线程类自带的join方法,将子线程加入到主线程,在子线程执行完之后,在执行主线程逻辑. 例如 public static void joinDemo() throws InterruptedException { System.out.println("=========Test with join=====");