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

Beej's Guide to Network Programming 系列連載 12:listen() — 伺服器端開始監聽

     編輯:關於C語言

前言:從「主動連線」轉為「被動等待」

伺服器端不主動連線,而是在特定埠口等待客戶端上門。這需要兩步驟:listen() 將 socket 設為「監聽模式」,accept() 取出等待佇列中的連線。本章聚焦 listen()。

函數原型

#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);

參數說明

  • sockfd:已 bind() 完成的 socket 描述符
  • backlog:等待佇列最大長度** — 已完成三向交握但尚未被 accept() 取走的連線數上限

backlog 參數深度解析

觀點說明
歷史 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);

回傳值與錯誤處理

  • 成功:回傳 0
  • 失敗:回傳 -1,設定 errno
  • 常見錯誤:
    • EADDRINUSE:埠口已被監聽 (可能忘記 SO_REUSEADDR)
    • EINVAL:socket 未綁定、或已經 listen 過
    • EOPNOTSUPP:socket 類型不支援 listen (如 SOCK_DGRAM)

重要觀念釐清

  1. listen() 不阻塞:它只是改變 socket 狀態,立即返回
  2. backlog 不是連線總數上限:只是「排隊等待 accept」的上限,已 accept 的連線不計入
  3. 佇列滿時:新連線收到 SYN 但不回 ACK (或送 RST),客戶端會重試
  4. UDP 不需 listen():無連線協議直接 recvfrom()

系統參數調優 (Linux)

# 臨時修改 (重開機失效)
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。

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