socket() 只是取得一個「通訊端點代號」,此時尚未綁定任何 IP 位址或埠號。伺服器端必須呼叫 bind() 將 socket 綁定到特定埠口,讓客戶端能找到並連線。客戶端通常不需呼叫 bind(),作業系統會在 connect() 時自動指派暫時埠號。
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
sockfd:socket() 回傳的檔案描述符my_addr:指向 sockaddr 結構 (實際為 sockaddr_in 或 sockaddr_in6),包含 IP 位址與埠號addrlen:my_addr 結構大小 (sizeof(struct sockaddr_in) 或 sizeof(struct sockaddr_in6))struct addrinfo hints, *res;
int sockfd;
// 1. 設定 hints:伺服器端需 AI_PASSIVE
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; // IPv4 或 IPv6
hints.ai_socktype = SOCK_STREAM; // TCP
hints.ai_flags = AI_PASSIVE; // 關鍵:自動填入本機 IP
// 2. 取得位址資訊 (node=NULL 表示本機所有介面)
getaddrinfo(NULL, "3490", &hints, &res);
// 3. 建立 socket
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
// 4. 綁定 - 直接使用 res 內容,零硬編碼
bind(sockfd, res->ai_addr, res->ai_addrlen);
// 5. 釋放資源
freeaddrinfo(res);
AI_PASSIVE + node=NULL → 綁定 本機所有介面 (IPv4: 0.0.0.0, IPv6: ::)AI_PASSIVE,將 IP 字串傳給 node 參數// IPv4 限定寫法
struct sockaddr_in myaddr;
sockfd = socket(PF_INET, SOCK_STREAM, 0);
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(3490);
myaddr.sin_addr.s_addr = INADDR_ANY; // 或 inet_addr("192.168.1.100")
memset(myaddr.sin_zero, 0, sizeof(myaddr.sin_zero));
bind(sockfd, (struct sockaddr*)&myaddr, sizeof(myaddr));
缺點:硬編碼 PF_INET/AF_INET、需手動 htons()、INADDR_ANY、sin_zero 清零,完全不支援 IPv6。
errnoEADDRINUSE:埠口已被佔用 (TIME_WAIT 狀態常見)EACCES:權限不足 (埠號 < 1024 需 root)EINVAL:socket 已經綁定過伺服器重啟時常遇到 EADDRINUSE,因前一次連線處於 TIME_WAIT 狀態 (預設 60 秒)。解決方案:
int yes = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
// 在 bind() 之前呼叫
bind() 是伺服器端「佔領埠口」的關鍵動作。現代寫法核心在於:**getaddrinfo() 設定 AI_PASSIVE + node=NULL,直接使用回傳的 ai_addr/ai_addrlen**。這不僅消除硬編碼,更讓同一套程式碼同時支援 IPv4 與 IPv6。下一章將探討 connect() — 客戶端如何主動發起連線。