電腦遊戲製作開發設計論壇 首頁 電腦遊戲製作開發設計論壇
任何可以在PC上跑的遊戲都可以討論,主要以遊戲之製作開發為主軸,希望讓台灣的遊戲人有個討論、交流、教學、經驗傳承的園地
 
 常見問題常見問題   搜尋搜尋   會員列表會員列表   會員群組會員群組   會員註冊會員註冊 
 個人資料個人資料   登入檢查您的私人訊息登入檢查您的私人訊息   登入登入 

Google
[範例]查詢自己主機的IP(C/C++)

 
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 網路程式設計
上一篇主題 :: 下一篇主題  
發表人 內容
還是零分
散播福音的祭司


註冊時間: 2007-09-19
文章: 164

652.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沒有其他方法了嗎?
回頂端
檢視會員個人資料 發送私人訊息 參觀發表人的個人網站
從之前的文章開始顯示:   
發表新主題   回覆主題    電腦遊戲製作開發設計論壇 首頁 -> 網路程式設計 所有的時間均為 台灣時間 (GMT + 8 小時)
1頁(共1頁)

 
前往:  
無法 在這個版面發表文章
無法 在這個版面回覆文章
無法 在這個版面編輯文章
無法 在這個版面刪除文章
無法 在這個版面進行投票
可以 在這個版面附加檔案
可以 在這個版面下載檔案


Powered by phpBB © 2001, 2005 phpBB Group
正體中文語系由 phpbb-tw 維護製作