伺服器端不主動連線,而是在特定埠口等待客戶端上門。這需要兩步驟:listen() 將 socket 設為「監聽模式」,accept() 取出等待佇列中的連線。本章聚焦 listen()。
#include <sys/types.h>
#include <sys/socket.h>
int listen(int sockfd, int backlog);
sockfd:已 bind() 完成的 socket 描述符backlog:等待佇列最大長度** — 已完成三向交握但尚未被 accept() 取走的連線數上限| 觀點 | 說明 |
|---|---|
| 歷史 BSD | 佇列包含「未完成連線」(SYN_RCVD) + 「已完成連線」(ESTABLISHED 待 accept) |
| 現代 Linux (TCP) | 分離為兩個佇列:net.ipv4.tcp_max_syn_backlog (半連線) + somaxconn (全連線) |
| 實際生效值 | min(backlog, /proc/sys/net/core/somaxconn),Linux 預設 somaxconn=128 |
| 建議值 | 高併發伺服器設為 1024 或更大,並調整系統參數 |
getaddrinfo(); // 解析位址
socket(); // 建立 socket
bind(); // 綁定埠口
listen(); // 進入監聽狀態
accept(); // 取出連線 (下一章詳細)
#define BACKLOG 128 // 建議值
struct addrinfo hints, *res;
int sockfd;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, "3490", &hints, &res);
sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
bind(sockfd, res->ai_addr, res->ai_addrlen);
// 關鍵:將 socket 設為被動監聽模式
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
printf("Server listening on port 3490...\n");
// 進入 accept 迴圈...
freeaddrinfo(res);
errnoEADDRINUSE:埠口已被監聽 (可能忘記 SO_REUSEADDR)EINVAL:socket 未綁定、或已經 listen 過EOPNOTSUPP:socket 類型不支援 listen (如 SOCK_DGRAM)recvfrom()# 臨時修改 (重開機失效)
sysctl -w net.core.somaxconn=4096
sysctl -w net.ipv4.tcp_max_syn_backlog=4096
# 永久修改 (/etc/sysctl.conf)
net.core.somaxconn = 4096
net.ipv4.tcp_max_syn_backlog = 4096
listen() 讓 socket 從「主動發起端」轉身為「被動等待端」。關鍵在於理解 backlog 的真實含義與系統限制,並配合 SO_REUSEADDR 避免重啟失敗。下一章將深入 accept() — 如何從佇列取出連線並獲得專屬通訊 socket。