還是零分 散播福音的祭司
註冊時間: 2007-09-19 文章: 164
653.83 果凍幣
|
發表於: 2011-8-29, PM 5:56 星期一 文章主題: [範例]查詢自己主機的IP(C/C++) |
|
|
以下內容是使用Winsock來查詢自己主機的IP
範例一我故意寫的很短
範例二是我深入研究之後補強的
如果有哪裡要糾正或幫忙補充的煩請不吝指教
範例一
代碼: |
#include <stdio.h>
#include <windows.h>
#include <winsock2.h>
#pragma comment (lib,"ws2_32.lib")
// 參數指標IPAddress用來指向IP字串,像"123.123.123.123"這樣子的字串
int GetHostIP( char *IPAddress )
{
char szHostName[128]; // 儲存主機的名稱
struct hostent *pHost; // 該結構用來裝我們要的資訊,不需要了解它有哪些成員
WSADATA wsaData; // "WSAStartup()"會在上面紀錄些訊息
WSAStartup( MAKEWORD(2,2), &wsaData ); // 將Winsock初始化,不然無法使用gethostname()
gethostname( szHostName, 128 ); // 獲得主機名稱
pHost = gethostbyname( szHostName ); // 再藉由主機名稱獲得我們要的資訊
WSACleanup(); // 將Winsock反初始化
// inet_ntoa()可將數字形式的IP轉成字串
IPAddress = inet_ntoa( *(struct in_addr*)pHost->h_addr_list[0] );
return 0;
} |
夠簡陋吧
"inet_ntoa()"這個函式可將數字形式的IP翻譯成字串
它回傳的是個指標(char*)
字串實際佔用的記憶體是該函式提供的
大概是靜態變數吧(這點要小心)
簡單說明一下數字型式的IP是長怎樣的
數字型式佔了4byte記憶體剛好填入IP的4個欄位數字
特別的是欄位是倒過來寫的
就像下面這樣
字串:"255.1.2.3"
則
數字:0x030201FF
"struct hostent"這個結構只要關心"h_addr_list"這個成員就夠了
"h_addr_list"是個指標陣列
每個指標指向一個無號整數(這講法其實不對,不過這樣比較好理解)
陣列的最末元素是個空指標
藉此表示陣列已經到底了
陣列上的每個無號整數代表一個IP
而通常我們就只找得到一個IP而已
因為那是主機在網際網路上使用的IP
若"h_addr_list"陣列還擁有其他IP的話
那麼這些多出來的IP是屬於區域網路使用的
是無法跟外界電腦連線的
區域網路IP範圍如下:(我不確定)
192.168.0.0 ~ 192.168.255.255
10.0.0.0 ~ 10.255.255.255
172.16.0.0 ~ 172.31.255.255
169.254.0.0 ~ 169.254.255.255
"h_addr_list"陣列把上面範圍的IP剔除之後就只剩下網際網路的IP了
所以那個簡陋程式還有幾個地方要改進
1.區域網路IP你應該不需要,寫個判斷式把它們跳過,只拿我們要的網際網路IP
2.還沒連線上網的時候是找不到IP的,要先判斷pHost->h_addr_list[0]是不是個空指標
3.inet_ntoa()提供的記憶體空間暫時使用就好,別將位址傳出去,以免出意外(其實"gethostbyname()"也有相同顧慮,不過我忽視它)
4.Winsock的初始化跟解除"應該"要先判斷Winsock目前是否已經初始化,這點可以利用"gethostname()"的回傳值來做判斷
改進以上缺點後再寫的正式一點、囉唆一點
就變成下面這麼肥了
範例二
代碼: |
#ifndef _WINSOCKAPI_
#define _WINSOCKAPI_ // 藉由定義_WINSOCKAPI_來避免windows.h去引入到舊版的WinSock
#endif
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winsock2.h>
#pragma comment (lib,"ws2_32.lib")
int GetHostIP( char *IPAddress )
{
int i = -1;
char szHostName[128]; // 儲存主機的名稱
WSADATA wsaData; // "WSAStartup()"會在上面紀錄些訊息
struct hostent *pHost; // 該結構用來裝我們要的資訊,不需要了解它有哪些成員
unsigned int uIPAddress; // 儲存IP,數字形式的IP
if( gethostname(szHostName,128) ) // 回傳值非零代表出問題了,問題應該是Winsock尚未初始化造成的
{
// 將Winsock初始化並做一些檢查動作
if( WSAStartup( MAKEWORD(2,2), &wsaData ) )
{
return 1; // WSAStartup()執行失敗
}
else
{
if( ( LOBYTE(wsaData.wVersion)!=2 ) || ( HIBYTE(wsaData.wVersion)!=2 ) )
{
WSACleanup();
return 2; // WinSock版本並非2.2版
}
}
if( gethostname(szHostName,128) )
{
WSACleanup();
return 3; // 問題很大
}
pHost = gethostbyname( szHostName ); // 再藉由主機名稱獲得我們要的資訊
WSACleanup();
}
else
{
pHost = gethostbyname( szHostName ); // 再藉由主機名稱獲得我們要的資訊
}
// 把區域網路的IP跳過,真不知道這迴圈該怎麼寫會比較漂亮
do
{
++i;
if( pHost->h_addr_list[i]==NULL ) // pHost->h_addr_list[]陣列最後一個元素為空指標
{
return 4; // 找不到對外的網路IP,很可能是因為還沒上網
}
uIPAddress = *(unsigned int*)pHost->h_addr_list[i];
}
while
(
((uIPAddress&65535)==0xA8C0) // 192.168.0.0~192.168.255.255
||
((uIPAddress&16777215)==16) // 10.0.0.0~10.255.255.255
||
(
// 172.16.0.0~172.31.255.255
((uIPAddress&255)==0xAC)// IP開頭是127
&&
(
((uIPAddress&65280)>0xF00)// 第二欄的值16以上
&&
((uIPAddress&65280)<0x2000)// 第二欄的值32未滿
)
)
||
((uIPAddress&65535)==0xFEA9) // 169.254.0.0~169.254.255.255
);
// IPAddress自己要準備空間來填IP,別用inet_ntoa()提供的空間會比較安心
strcpy( IPAddress, inet_ntoa( *(struct in_addr*)&uIPAddress ) );
return 0;
} |
最後只剩幾個疑惑
1.這方法找到的IP只有區域網路的不能用嗎?還是有其他IP也是要跳過去的?
2.除了Winsock沒有其他方法了嗎? |
|