1. 程式人生 > >多網絡卡情況下如何正確獲得IP

多網絡卡情況下如何正確獲得IP

     這幾天在自己的個人機器上裝了個虛擬機器VMWare。然後在程式中獲得本地IP,發現,是錯誤的,發現獲得的IP竟然不是原來的192.168.1.155,而是一個奇怪的192.168.233.1。後來發現,原來自己裝了虛擬機器,這個地址是自己虛擬機器VMWare VMNet8的地址,而且,這個網路被命名為本地連線2。

     看下我自己原來的程式碼,是用的gethostbyname方法獲得的。於是,寫了個Demo程式,進行執行

#include<WinSock2.h>
#include<windef.h>
#include<ws2tcpip.h>
#include<stdio.h>

int main(int argc, char *argv[])
{
	char *ptr, **pptr;
	struct hostent *hptr;
	struct hostent *pHPtr;
	char str[32];
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD( 2, 0 );

	char name[100];

	ptr = name;

	if (0 ==  WSAStartup( wVersionRequested, &wsaData ) )
	{

		if(0 != gethostname ( ptr, strlen(ptr)) )
		{
			printf("沒有獲得名稱\n");
			return -1;
		}

		if (NULL == (hptr = gethostbyname(ptr)))
		{
			printf("獲得地址錯誤\n");
			return -1;
		}

		printf("機器的正式名字是: %s\n", ptr);

		for (pptr = hptr->h_aliases; *pptr != NULL; pptr++)
		{
			printf("別名是: %s\n", *pptr);
		}

		int i = 1;

		switch(hptr->h_addrtype)
		{
		case AF_INET:
		case AF_INET6:
			pptr = hptr->h_addr_list;
			
			for(; *pptr != NULL; pptr++)
			{
				printf("地址是: %s\n", inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
			}

			pHPtr = hptr;
						
			for (pptr = hptr->h_addr_list; *pptr != NULL; pptr++)
			{
				//inet_ntop的實質
				printf("第%d 個地址是: %d.%d.%d.%d\n", i, pHPtr->h_addr_list[i-1][0] & 0x00ff, pHPtr->h_addr_list[i-1][1] & 0x00ff, 
					pHPtr->h_addr_list[i-1][2] & 0x00ff, pHPtr->h_addr_list[i-1][3]& 0x00ff);
				printf("第%d 個地址是: %s\n", i++, inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)) );
			}

			printf("第一個地址是: %s\n", inet_ntop(hptr->h_addrtype, hptr->h_addr, str, sizeof(str)));
			break;

		default:
			printf("未知地址型別\n");
		}

		WSACleanup( );
	}	

	return 0;
}

執行結果如圖所示:


原來,第一個地址早已不是我自己的本地地址,而是VMNet8 的地址。

我查下相應函式的用法,相應資訊如下:

struct hostent *gethostbyname(const char *name);
    這個函式的傳入值是域名或者主機名,例如"www.google.cn"等等。傳出值,是一個hostent的結構。如果函式呼叫失敗,將返回NULL。
    返回hostent結構體型別指標
    struct hostent
    {
        char    *h_name;               
        char    **h_aliases;
        int     h_addrtype;
        int     h_length;
        char    **h_addr_list;
        #define h_addr h_addr_list[0]
    };
    hostent->h_name
    表示的是主機的規範名。例如www.google.com的規範名其實是www.l.google.com。    
    hostent->h_aliases
    表示的是主機的別名.www.google.com就是google他自己的別名。有的時候,有的主機可能有好幾個別名,這些,其實都是為了易於使用者記憶而為自己的網站多取的名字。

    hostent->h_addrtype    
    表示的是主機ip地址的型別,到底是ipv4(AF_INET),還是pv6(AF_INET6)    

hostent->h_length      
    表示的是主機ip地址的長度

  ——————————————以上是分割線————————————————————————————

可見,通過gethostbyname 的方法獲取IP的方法也是可以的,只是這個VMNet 8地址確實排在前面,我無法修改。

而且gethostbyname是遞迴通過DNS阻塞查詢主機名和IP,很影響效率。據說好多公司做爬蟲都是自己查詢主機名和網站名的IP,不用gethostbyname,就是因為gethostbyname效率太低了。

繼續查資料。

發現一個函式GetAdaptersInfo,getAdapterInfo相應參考資料如下:

GetAdaptersInfo函式:

DWORD GetAdaptersInfo(
  // 接受資料的緩衝區,指向一個結構IP_ADAPTER_INFO
  PIP_ADAPTER_INFO pAdapterInfo,  
  // 指向輸出緩衝區的大小的指標
  PULONG pOutBufLen              
);

這個函式中最重要是第一個引數,看看這個結構有點啥?MSDN中說明如下:

typedef struct _IP_ADAPTER_INFO {
  struct _IP_ADAPTER_INFO* Next; // 連結串列指標,指向下一個單元
  DWORD ComboIndex;
  char AdapterName[MAX_ADAPTER_NAME_LENGTH + 4]; // 介面資訊物理名稱
  char Description[MAX_ADAPTER_DESCRIPTION_LENGTH + 4];// 介面描述資訊
  UINT AddressLength; // MAC地址的長度
  BYTE Address[MAX_ADAPTER_ADDRESS_LENGTH]; // MAC地址
  DWORD Index;
  UINT Type;
  UINT DhcpEnabled;
  PIP_ADDR_STRING CurrentIpAddress;
  IP_ADDR_STRING IpAddressList;  // IP地址列表
  IP_ADDR_STRING GatewayList;   // 閘道器地址列表
  IP_ADDR_STRING DhcpServer;   // DHCP地址
  BOOL HaveWins;
  IP_ADDR_STRING PrimaryWinsServer;   // 首選WINS 伺服器
  IP_ADDR_STRING SecondaryWinsServer; // 備用WINS伺服器
  time_t LeaseObtained;
  time_t LeaseExpires;
} IP_ADAPTER_INFO, *PIP_ADAPTER_INFO;
————————————————————以上是分割線———————————————————— 於是,參照相應樣例,寫出如下程式碼:
#include<winsock.h>
#include<stdio.h>
#include<IPHlpApi.h>

#pragma comment(lib, "IPHlpApi.Lib")

int main()
{
	IP_ADAPTER_INFO IOInfo[20];
	PIP_ADAPTER_INFO pIOInfo = NULL;
	DWORD Result = 0;
	unsigned long nLen = sizeof(IOInfo);

	Result = GetAdaptersInfo(IOInfo, &nLen);

	if (NO_ERROR != Result)
	{
		fprintf(stderr, "GetAdaptersInfo Error!\n");
		return -1;
	}
	else
	{
		pIOInfo = IOInfo;

		int Num = 0;
		while (pIOInfo != NULL)
		{
			fprintf(stdout, "\n--------------------Num.%d-------------------------\n", Num);
			printf("|Name:%s\n", pIOInfo->AdapterName);
			printf("|Desc:%s\n", pIOInfo->Description);
			printf("|IP:%s\n", pIOInfo->IpAddressList.IpAddress.String);
			printf("|MAC:%02X:%02X:%02X:%02X:%02X:%02X\n", pIOInfo->Address[0], pIOInfo->Address[1], pIOInfo->Address[2],
				pIOInfo->Address[3], pIOInfo->Address[4], pIOInfo->Address[5]);
			printf("|GateWay:%s\n", pIOInfo->GatewayList.IpAddress.String);
			printf("--------------------Num.%d-------------------------", Num++);

			pIOInfo = pIOInfo->Next;
		}
	}

	return 0;
}
執行結果如下:
我看到,執行程式確實區分出了VM的網絡卡和本地網絡卡;無奈本地網絡卡很有可能變,而本地網絡卡的Desc不足以填充足夠的資訊,從而為作為整個資訊的區分。 我突然想到IPConfig命令,可以完全顯示出網絡卡和網路的名稱,於是,收啟發,想進行IPConfig命令,然後獲取資訊,再獲得IP地址。 在我的機器上執行ipconfig /all 命令,獲得結果如下:
Windows IP 配置

   主機名  . . . . . . . . . . . . . : USER-20151221DQ
   主 DNS 字尾 . . . . . . . . . . . : 
   節點型別  . . . . . . . . . . . . : 混合
   IP 路由已啟用 . . . . . . . . . . : 否
   WINS 代理已啟用 . . . . . . . . . : 否

乙太網介面卡 本地連線 2:

   連線特定的 DNS 字尾 . . . . . . . : localdomain
   描述. . . . . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet8
   實體地址. . . . . . . . . . . . . : 00-50-56-C0-00-08
   DHCP 已啟用 . . . . . . . . . . . : 是
   自動配置已啟用. . . . . . . . . . : 是
   本地連結 IPv6 地址. . . . . . . . : fe80::643d:e61d:7f3:fb4d%15(首選) 
   IPv4 地址 . . . . . . . . . . . . : 192.168.233.1(首選) 
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
   獲得租約的時間  . . . . . . . . . : 2016年7月19日 9:26:32
   租約過期的時間  . . . . . . . . . : 2016年7月19日 13:41:32
   預設閘道器. . . . . . . . . . . . . : 
   DHCP 伺服器 . . . . . . . . . . . : 192.168.233.254
   DHCPv6 IAID . . . . . . . . . . . : 318787670
   DHCPv6 客戶端 DUID  . . . . . . . : 00-01-00-01-1E-09-9D-1C-2C-41-38-8E-9C-F8
   DNS 伺服器  . . . . . . . . . . . : 192.168.233.2
   主 WINS 伺服器  . . . . . . . . . : 192.168.233.2
   TCPIP 上的 NetBIOS  . . . . . . . : 已啟用

乙太網介面卡 VMware Network Adapter VMnet1:

   連線特定的 DNS 字尾 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : VMware Virtual Ethernet Adapter for VMnet1
   實體地址. . . . . . . . . . . . . : 00-50-56-C0-00-01
   DHCP 已啟用 . . . . . . . . . . . : 否
   自動配置已啟用. . . . . . . . . . : 是
   本地連結 IPv6 地址. . . . . . . . : fe80::acef:74f3:abe2:7f54%13(首選) 
   IPv4 地址 . . . . . . . . . . . . : 192.168.254.1(首選) 
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
   預設閘道器. . . . . . . . . . . . . : 
   DHCPv6 IAID . . . . . . . . . . . : 302010454
   DHCPv6 客戶端 DUID  . . . . . . . : 00-01-00-01-1E-09-9D-1C-2C-41-38-8E-9C-F8
   DNS 伺服器  . . . . . . . . . . . : fec0:0:0:ffff::1%1
                                       fec0:0:0:ffff::2%1
                                       fec0:0:0:ffff::3%1
   TCPIP 上的 NetBIOS  . . . . . . . : 已啟用

乙太網介面卡 本地連線:

   連線特定的 DNS 字尾 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Intel(R) 82579LM Gigabit Network Connection
   實體地址. . . . . . . . . . . . . : 2C-41-38-8E-9C-F8
   DHCP 已啟用 . . . . . . . . . . . : 否
   自動配置已啟用. . . . . . . . . . : 是
   本地連結 IPv6 地址. . . . . . . . : fe80::6822:7d71:8cda:2843%11(首選) 
   IPv4 地址 . . . . . . . . . . . . : 192.168.1.155(首選) 
   子網掩碼  . . . . . . . . . . . . : 255.255.255.0
   預設閘道器. . . . . . . . . . . . . : 192.168.1.1
   DHCPv6 IAID . . . . . . . . . . . : 237781304
   DHCPv6 客戶端 DUID  . . . . . . . : 00-01-00-01-1E-09-9D-1C-2C-41-38-8E-9C-F8
   DNS 伺服器  . . . . . . . . . . . : fec0:0:0:ffff::1%1
                                       fec0:0:0:ffff::2%1
                                       fec0:0:0:ffff::3%1
   TCPIP 上的 NetBIOS  . . . . . . . : 已啟用

隧道介面卡 isatap.{A5D17715-BE7B-4932-9B76-1F96CFE71258}:

   媒體狀態  . . . . . . . . . . . . : 媒體已斷開
   連線特定的 DNS 字尾 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter
   實體地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
   DHCP 已啟用 . . . . . . . . . . . : 否
   自動配置已啟用. . . . . . . . . . : 是

隧道介面卡 isatap.{EFE41274-D6A2-40A2-B625-131EA423FCD6}:

   媒體狀態  . . . . . . . . . . . . : 媒體已斷開
   連線特定的 DNS 字尾 . . . . . . . : 
   描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #2
   實體地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
   DHCP 已啟用 . . . . . . . . . . . : 否
   自動配置已啟用. . . . . . . . . . : 是

隧道介面卡 isatap.localdomain:

   媒體狀態  . . . . . . . . . . . . : 媒體已斷開
   連線特定的 DNS 字尾 . . . . . . . : localdomain
   描述. . . . . . . . . . . . . . . : Microsoft ISATAP Adapter #3
   實體地址. . . . . . . . . . . . . : 00-00-00-00-00-00-00-E0
   DHCP 已啟用 . . . . . . . . . . . : 否
   自動配置已啟用. . . . . . . . . . : 是
-E0
   DHCP 已啟用 . . . . .
通過這個,我可以清楚的看到,我的首個IP確實是命名為本地連線2  的 IP,而不是我的本地IP;但是這個上面的網路名稱絕對不錯,“本地連線”這個網路IP是:192.168.1.155 於是,用著執行這個ipconfig的方法的程式,發現,知道一個system函式。發現此函式可以執行命令,但是,顯示不出來命令的回顯。目前也找不到更好的方法 ,於是,我想到的方法是,把命令回視訊記憶體到相應檔案裡,然後,再進行讀取後獲得IP。 查詢到一個函式GetCurrentDirectory,然後確保執行的程式建立的檔案在相應的程式的路徑下,可以進行靈活寫入。 整個程式如下:
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<string>
#include<Windows.h>
//#include<WinBase.h>

#ifndef MAXTHPATH
#define MAXTHPATH (256)
#endif

#ifndef IPNUM
#define IPNUM (20)
#endif

#ifndef FILENAME
#define FILENAME ("/NetMask.txt")
#endif

char *getLocalIP(char *pszIP);

int main(int argc, char *argv[])
{
	char pszIP[IPNUM] = {0};
	getLocalIP(pszIP);
	printf("本地IP為 %s\n", pszIP);

	//delete []buf;

	return 0;
}

char *getLocalIP(char *pszIP)
{
	char pPath[MAXTHPATH];
	GetCurrentDirectory(MAXTHPATH, pPath);

	strcat(pPath, FILENAME);
	
	char pszCmd[MAXTHPATH] = {0};
	strcpy(pszCmd, "ipconfig /all > ");
	strcpy(pszCmd + strlen(pszCmd), pPath);
	
	//執行DOS命令,並把網路資訊輸出到資料夾下
	system(pszCmd);

	FILE *fp = fopen(pPath, "r");
	fseek(fp, 0, SEEK_END);
	int len = ftell(fp);
	rewind(fp);
	char *pBuf = new char[len+1];
	memset(pBuf, 0, len +1);
	fread(pBuf, 1, len, fp);
	fclose(fp);
	
	//根據一個機器獲得,如果DOS命令變更,可能會發生更改
	char *pLAN = strstr(pBuf, "本地連線:");
	char *pIPV4 = strstr(pLAN, "IPv4 地址");
	pIPV4 = strstr(pIPV4, ":");
	pIPV4 += 2; //翻過“:”符號,獲得真正的IP

	char *pEnd = strstr(pIPV4, "(");

	strncpy(pszIP, pIPV4, pEnd - pIPV4);

	//避免記憶體洩露
	delete []pBuf;

	return pszIP;
}
執行結果如下:
顯然,得到了正確的IP。 幾個值得注意的事情是: 1)在Linux下,此命令應該改為ifconfig; 2)其他機器不知道相應的回顯字串格式是否正確,還有,機器的語言由中文換成英文後,是否能有正確的支援; 3)system函式無法把回顯直接輸出到程式裡; 4)最後獲得IP的過程,讀取字串可以用正則;正則匹配通用性更好,但在windows下不支援正則,要想進行支援,需要新增boost庫進行支援 5)有沒有更好的直接獲取本地IP的方法,雖然此種方法可以獲得多網絡卡,尤其是實際的物理多網絡卡的IP地址

相關推薦

情況如何正確獲得IP

     這幾天在自己的個人機器上裝了個虛擬機器VMWare。然後在程式中獲得本地IP,發現,是錯誤的,發現獲得的IP竟然不是原來的192.168.1.155,而是一個奇怪的192.168.233.1。後來發現,原來自己裝了虛擬機器,這個地址是自己虛擬機器VMWare VM

情況獲得所有的IP地址

在編寫基於sock的網路程式時,有時需列舉系統中繫結的所有IP地址,在一般控制元件無法解決些問題的情況下,可使用如下方法一試:一、基於winsock的方法,例程如下(修改自網上一個名為“GetIP”的程式原始碼):// GetIP.cpp : Defines the entr

Eureka的自我保護模式、環境IP選擇和健康檢查

Eureka的自我保護模式 禁用自我保護模式: eureka.server.enable-self-preservation = false 多網絡卡環境下的IP選擇 對於多網絡卡的伺服器,各個微服務註冊到Eureka Server上的IP要如何指定呢? 指定IP在某些場合下

Spring Cloud 環境Eureka服務註冊IP選擇問題

問題場景 伺服器上分別配置了eth0, eth1和eth2三塊網絡卡,只有eth1的地址可供其它機器訪問,eth0和eth2的 IP 無效。在這種情況下,服務註冊時Eureka Client會自動選擇eth0作為服務ip, 導致其它服務無法呼叫由於官方並沒有寫明Eureka

解決環境使用特定廣播UDP訊息的問題

多網絡卡環境下發送UDP廣播到特定網路通常有以下幾種方式: 將socket繫結到特定網絡卡ip; 使用socket選項; 遍歷網絡卡; 設定路由表。 繫結到特定IP 建立socket後將其繫結到特定IP地址,則傳送廣播詳細的時候會通過此網絡卡傳送。如果我們的

情況通過路由表規則指定訪問特定ip

今天,同事阿峰外出駐點,而我在公司辦公,他想像以往一樣用teamviewer控制我的電腦去除錯公司的伺服器,我說沒問題。 然而,

Linux不同IP在同一段的情況

關鍵字:Strict Interface ARP, Multi-link, Multipath, Multiple network cards on same subnet problem 公司那個提供音樂下載的域名流量直逼1Gbps,但是系統的連線卻不高,伺服器用的是DELL2850的,板載兩個Gbe的

存在情況獲取指定的MAC地址

應用軟體中經常有需要顯示mac地址的功能,方便使用者判斷當前聯網狀態,如果是在多網絡卡存在(無線,有線等)下,網路會經常切換,獲取到的MAC地址可能與預期不同,現給出指定網絡卡獲取mac地址的方法。 void get_mac(Mac_Address &am

CentOS繫結bond/聚合

網絡卡bond我直接理解成網絡卡聚合了,就是把多張網絡卡虛擬成1張網絡卡,出口時,這張網絡卡無論哪個斷線都不影響網路,入口時,需要結合交換機的埠聚合功能實現和網絡卡配置的bond模式進行負載均衡。bond需要在核心Kernels 2.4.12及以上才能使用,因為需要使用bonding模組。 bond模式:

Eureka客戶端註冊IP選擇問題

.title { background: #5EA2A2; color: #FFFFFF; font-family: "微軟雅黑", "宋體", "黑體", Arial; font-size: 17px; font-weight: bold; height: 25px; line-height: 25px;

Centos7的路由轉發配置

Centos7前的版本用iptable 防火牆設定路由轉發 ;Centos7 以後的版本不能使用iptable 防火牆來設定路由器轉發,而是得用firewall-cmd 來設定 首先輸入firewall-cmd --list-all 來檢視當前防火牆配置 假設介面 inte

linux路由設定

在linux多網絡卡情況下,如不能正確設定路由將導致部分網路不通。 一、檢視路由 使用命令: route 二、預設路由設定 1、刪除預設路由 route del default 2、增加預設路由 route add default gw IP(如:192.168.1.1)

【shell】海思3536 繫結聚合——bond技術

0.原理說明 目前網絡卡繫結mode共有七種(0~6)bond0、bond1、bond2、bond3、bond4、bond5、bond6 常用的有三種: mode=0:平衡負載模式,有自動備援,但需要”Switch”支援及設定。 mode=1:自動備援模式,其中一條線若斷線,其他線路

spring cloud EurekaClient ip 配置 和 原始碼分析

1、前言 對於spring cloud,各個服務例項需要註冊到Eureka註冊中心。 一般會配置ip註冊,即eureka.instance.prefer-ip-address=true。 但是,如果服務例項所在的環境存在多個網絡卡,經常會出現註冊過去的ip不是我們想要的ip。

c# 由【網路介面卡名】獲取資訊,IP

c# 多網絡卡 由【網路介面卡名】獲取網絡卡資訊,IP 多網絡卡電腦中,網路介面卡的名字 多樣化! 專案中需要,根據網路介面卡 名字 獲取 單個網絡卡的IP: using System.Net.NetworkInformation;

Linux中高階網路配置-team方式繫結

team簡介 在redhat7.0以上版本,配置多網絡卡繫結時,專門提供了team工具來實現多網絡卡的繫結。 team :也是鏈路聚合 最多支援8塊網絡卡,不需要手動載入相應核心模組 支援模式: broadcast 廣播容錯 roundrobin 輪詢

Ubuntu伺服器安裝Kubernetes

多網絡卡伺服器安裝Kubernetes如何指定叢集選用的IP地址呢? 指定--apiserver-advertise-address引數即可。如下: sudo kubeadm init --kubernetes-version=v1.12.2 --apiserver-advertise-ad

Ubuntu 18.04 設定埠聚合

多網絡卡聚合把多個網路埠繫結到一個IP地址,可以提高網路總頻寬和容錯能力。 Ubuntu 18.04使用了netplan來管理網路,跟以前版本差別很大。以前版本修改/etc/network/interfaces的方法不再管用,設定多網絡卡聚合的方式也有點不一樣了,不過實際用起來比以前還

雲伺服器配置實踐

環境概述: 系統環境:標準型S2、1核2G、1Mbps、Centos7.4 64位、騰訊雲、北京一區、VPC(私有網路) 遠端連線工具:xshell5 目標: 為伺服器配置 2個網絡卡,每個網絡卡配置2個內網ip、2個外網ip,共計4個外網ip、4

02 Linux 繫結

 Linux 多網絡卡繫結 網絡卡繫結mode共有七種(0~6) bond0、bond1、bond2、bond3、bond4、bond5、bond6 常用的有三種 mode=0:平衡負載模式,有自動備援,但需要”Switch”支援及設定。 mode=1:自