在 TCP/IP 網路程式設計中,Socket 位址結構是連線建立、資料收發的基石。本文系統整理 sockaddr 通用結構、sockaddr_in IPv4 專用結構、in_addr IP 位址封裝,並詳細說明網路位元組序與主機位元組序的轉換函數,幫助開發者建立清晰的記憶體佈局心智模型。
sockaddrstruct sockaddr {
u_short sa_family; // 位址族,如 AF_INET、AF_INET6、AF_UNIX
char sa_data[14]; // 14 位元組協議專用位址內容
};
sa_data 的具體解釋隨 sa_family 而異,長度固定 14 位元組僅為歷史妥協,實際 IPv4 只需 8 位元組,IPv6 需 28 位元組,Unix 域套接字甚至可達 110 位元組。因此 bind()、connect() 等 API 必須額外傳遞 socklen_t addrlen 參數明確指定結構長度。
sockaddr_instruct in_addr {
u_long s_addr; // 32 位元 IP 位址,網路位元組序
};
struct sockaddr_in {
short sin_family; // AF_INET
u_short sin_port; // 16 位元埠號,網路位元組序
struct in_addr sin_addr; // 32 位元 IP 位址
char sin_zero[8]; // 填充對齊,保留未用
};
記憶體佈局共 16 位元組:sin_family(2) + sin_port(2) + sin_addr(4) + sin_zero(8)。實際使用時常以 sockaddr* 指標傳遞,靠 sa_family 辨識實際類型。
網路位元組序(Big-Endian)為跨平台協議標準;主機位元組序視 CPU 架構而定(x86 為 Little-Endian)。四大轉換函數:
| 函數 | 方向 | 位元寬度 | 典型用途 |
|---|---|---|---|
htons() | Host → Network | 16-bit | 埠號 sin_port |
htonl() | Host → Network | 32-bit | IP 位址 s_addr |
ntohs() | Network → Host | 16-bit | 解析收到的埠號 |
ntohl() | Network → Host | 32-bit | 解析收到的 IP 位址 |
口訣:h=host、n=network、s=short(16)、l=long(32)。
sin_port/s_addr,導致連線失敗或位址錯誤。sin_zero 未 memset(&addr, 0, sizeof(addr)),可能攜帶垃圾資料影響 bind。bind(sock, (sockaddr*)&addr, sizeof(addr)) 時務必強制轉型,並傳遞正確長度。bind 回傳 -1 時檢查 WSAGetLastError(),常見 WSAEADDRINUSE(埠被佔用)、WSAEINVAL(參數無效)。sockaddr_in6struct sockaddr_in6 {
u_short sin6_family; // AF_INET6
u_short sin6_port; // 埠號
u_long sin6_flowinfo; // 流資訊
struct in6_addr sin6_addr; // 128 位元 IP
u_long sin6_scope_id; // 介面索引
};
現代程式建議使用 getaddrinfo() 統一解析域名/位址,自動填充 sockaddr_storage 以相容 IPv4/IPv6 雙棧。
掌握 Socket 位址結構的記憶體佈局、位元組序轉換、長度參數用途,是編寫健壯網路程式的前提。建議封裝 AddrToString/StringToAddr 工具函數,統一處理轉換與格式化,減少手動填結構的錯誤機率。