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

方法一:

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

参考资料:

posted @ 2019-04-04 14:09  一路一沙  阅读(1255)  评论(0编辑  收藏  举报