在現代網路環境中,伺服器或工作站往往配置多張網卡,或透過 DHCP 動態取得多組 IP 位址。這類「多重綁定主機」擁有多個網路介面,各自綁定不同的 IP。開發網路應用程式時,如何在程式中動態取得本機所有有效的 IP 位址,是一項基礎且關鍵的技術需求。
Windows 平台提供 Winsock API,讓我們能輕鬆查詢主機名稱並解析其對應的 IP 列表。以下是完整的實作步驟與程式碼範例:
首先呼叫 gethostname() 取得本機的標準主機名稱,這是後續查詢的鑰匙。
呼叫 gethostbyname() 傳入主機名稱,即可取得 hostent 結構指標。該結構定義如下:
struct hostent {
char FAR * h_name; // 官方主機名稱
char FAR * FAR * h_aliases; // 別名列表
short h_addrtype; // 位址類型 (AF_INET)
short h_length; // 位址長度 (IPv4 為 4)
char FAR * FAR * h_addr_list; // IP 位址指標陣列 (以 NULL 結尾)
};
h_addr_list 是一個指標陣列,每個元素指向一個 in_addr 結構。我們需逐一取出,並用 inet_ntoa() 轉為可讀的點分十進制字串。
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
void PrintAllLocalIPs() {
char szHostName[128];
struct hostent *pHostEnt;
int i = 0;
// 初始化 Winsock (僅需呼叫一次)
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("Winsock 初始化失敗。\n");
return;
}
// 1. 取得主機名稱
if (gethostname(szHostName, sizeof(szHostName)) == 0) {
// 2. 解析主機資訊
pHostEnt = gethostbyname(szHostName);
if (pHostEnt != NULL) {
printf("主機名稱: %s\n", pHostEnt->h_name);
printf("IP 位址列表:\n");
// 3. 遍歷 h_addr_list
for (i = 0; pHostEnt->h_addr_list[i] != NULL; ++i) {
struct in_addr addr;
memcpy(&addr, pHostEnt->h_addr_list[i], sizeof(struct in_addr));
printf(" [%d] %s\n", i + 1, inet_ntoa(addr));
}
} else {
printf("gethostbyname 失敗,錯誤碼: %d\n", WSAGetLastError());
}
} else {
printf("gethostname 失敗,錯誤碼: %d\n", WSAGetLastError());
}
WSACleanup();
}
int main() {
PrintAllLocalIPs();
return 0;
}
WSAStartup,結束前呼叫 WSACleanup,否則 API 呼叫會失敗。AF_INET)。若需支援 IPv6,請改用 getaddrinfo() 這類新式 API,並檢查 h_addrtype 是否為 AF_INET6。inet_ntoa 的執行緒安全性:此函式使用靜態緩衝區,多執行緒環境下請改用 InetNtop 或自行管理緩衝區。GetAdaptersAddresses 取得介面索引、閘道、網段等資訊,實作更精細的網路路由控制。gethostname/gethostbyname,標頭檔換為 <netdb.h>、<arpa/inet.h> 即可。掌握 gethostname → gethostbyname → 遍歷 h_addr_list 這條經典路徑,是 Windows 網路程式設計的基本功。雖然現代開發建議改用 GetAdaptersAddresses 或 getaddrinfo 以獲得更豐富的介面資訊與 IPv6 支援,但理解傳統 API 的運作機制,對除錯舊代碼、快速原型開發依然極具價值。