首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Beej's Guide to Network Programming 系列連載 10:bind() — 綁定埠口的關鍵步驟

     編輯:關於C語言

前言:為什麼需要 bind()?

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))

現代實戰範例:搭配 getaddrinfo() 零硬編碼

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

  • AI_PASSIVE + node=NULL → 綁定 本機所有介面 (IPv4: 0.0.0.0, IPv6: ::)
  • 若需綁定特定 IP:移除 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。

回傳值與錯誤處理

  • 成功:回傳 0
  • 失敗:回傳 -1,設定 errno
  • 常見錯誤:
    • EADDRINUSE:埠口已被佔用 (TIME_WAIT 狀態常見)
    • EACCES:權限不足 (埠號 < 1024 需 root)
    • EINVAL:socket 已經綁定過

SO_REUSEADDR:解決「Address already in use」

伺服器重啟時常遇到 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() — 客戶端如何主動發起連線。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved