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

Linux Socket 網路程式設計完整指南:從基礎 API 到 TCP 連接管理實戰

     編輯:關於C

「一切皆 Socket」。現代網路程式幾乎全建立在 BSD Socket API 之上。本文系統梳理 Socket 核心概念、基礎 API 全家桶、TCP 三次握手/四次揮手與 Socket 函數的對應關係、完整 Client/Server 範例、併發模型演進方向。

一、網路進程識別:三元組

  • IP 位址:網路層識別主機
  • 協議 + 埠口:傳輸層識別進程(TCP/UDP 各自埠口空間)
  • 三元組 (IP, 協議, Port) 唯一標識網路進程

二、Socket 核心 API 詳解

1. socket():建立描述符


int socket(int domain, int type, int protocol);
// domain: AF_INET(IPv4), AF_INET6, AF_UNIX(本地)
// type: SOCK_STREAM(TCP), SOCK_DGRAM(UDP), SOCK_RAW
// protocol: 0=自動選預設, IPPROTO_TCP, IPPROTO_UDP
// 返回: socket fd (非負整數), -1 失敗

2. bind():綁定地址


int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
// sockaddr_in (IPv4): sin_family, sin_port(網路序), sin_addr
// sockaddr_in6 (IPv6): sin6_family, sin6_port, sin6_addr, sin6_scope_id
// sockaddr_un (Unix): sun_family, sun_path
// 伺服器必須 bind 知名埠口;客戶端通常不 bind(連線時自動分配)

3. 位元組序轉換:關鍵細節


// 主機序 → 網路序 (大端)
uint16_t port_net = htons(6666);  // host to network short
uint32_t ip_net = htonl(INADDR_ANY); // host to network long

// 網路序 → 主機序
uint16_t port_host = ntohs(addr.sin_port);
uint32_t ip_host = ntohl(addr.sin_addr.s_addr);

鐵律:所有進出網路的整數(埠口、IP、長度)必須轉網路序,勿假設主機序即網路序。

4. listen() / connect():連線建立


int listen(int sockfd, int backlog); // backlog: 已完成連線佇列上限
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
// listen 將主動 socket 轉為被動監聽
// connect 發起主動連線(阻塞直到三次握手完成/失敗)

5. accept():接受連線


int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// 從已完成佇列取出連線,返回**新 fd**(已連線 socket)
// 原 sockfd 繼續監聽(監聽 socket)
// 典型模式:while ((connfd = accept(listenfd, ...)) > 0) { 處理 connfd; close(connfd); }

6. I/O 多路複用組

函數對特點
read/write通用檔案描述符,阻塞
recv/sendSocket 專用,支援 flags (MSG_OOB, MSG_PEEK, MSG_DONTWAIT)
readv/writev分散/聚合 I/O,單次系統調用多緩衝區
recvmsg/sendmsg最通用,支援控制資料 (cmsg)、檔案描述符傳遞
recvfrom/sendto無連線 (UDP) 指定對端地址

推薦:recvmsg/sendmsg 統一介面,其餘可視為特例。

7. close():關閉描述符

  • 引用計數 -1,歸零時觸發 TCP FIN 發送
  • shutdown(fd, SHUT_WR):主動發 FIN,但仍可讀(半關閉)
  • SO_LINGER 選項控制關閉行為

三、TCP 連接狀態機與 Socket 對應

三次握手


Client: connect() → 發 SYN → 阻塞
Server: accept() → 收 SYN → 回 SYN+ACK → 阻塞
Client: 收 SYN+ACK → 回 ACK → connect() 返回
Server: 收 ACK → accept() 返回

關鍵點:connect() 在**第二次握手**返回,accept() 在**第三次握手**返回。

四次揮手


主動關閉方: close() → 發 FIN → 進 FIN_WAIT_1
被動方: 收 FIN → 回 ACK → 進 CLOSE_WAIT → 應用 close() → 發 FIN → 進 LAST_ACK
主動方: 收 FIN → 回 ACK → 進 TIME_WAIT (2MSL) → CLOSED

四、完整範例:TCP Echo Server / Client

Server (Iterative)


#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define PORT 6666
#define MAXLINE 4096

int main() {
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in servaddr = {0};
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);

    bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    listen(listenfd, 10);
    printf("====== waiting for client ======\n");

    while (1) {
        int connfd = accept(listenfd, NULL, NULL);
        char buf[MAXLINE];
        int n = recv(connfd, buf, MAXLINE, 0);
        buf[n] = '\0';
        printf("recv: %s\n", buf);
        close(connfd);
    }
    close(listenfd);
    return 0;
}

Client


int main(int argc, char** argv) {
    if (argc != 2) { fprintf(stderr, "usage: %s \n", argv[0]); exit(1); }

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in servaddr = {0};
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(PORT);
    inet_pton(AF_INET, argv[1], &servaddr.sin_addr);

    connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
    printf("send msg: ");
    char sendline[MAXLINE];
    fgets(sendline, MAXLINE, stdin);
    send(sockfd, sendline, strlen(sendline), 0);
    close(sockfd);
    return 0;
}

五、併發模型演進:從迭代到高效能

模型適用場景缺點
迭代 (範例)教學、極低併發串行處理,吞吐極低
fork 多進程簡單隔離進程開銷大,IPC 複雜
pthread 多執行緒高併發、共享記憶體鎖競爭、堆疊記憶體
I/O 多路複用 (select/poll/epoll)單執行緒高併發 (C10K)邏輯複雜、阻塞調用難處理
非阻塞 + 事件驅動 (libevent, libuv, io_uring)極高併發 (C10M)學習曲線陡峭

六、實戰練習題

需求:
Server 綁定 192.168.100.168:6666,僅接受來自 192.168.100.2 的連線。
Client 依序發送:"Client Query test"、"Client Query"、"Client Query Quit" 後退出。
Server 收到含 "Client Query" 字串則列印 "Receive Query"。

提示:Server 需在 accept() 後檢查 addr.sin_addr.s_addr,Client 需 send() 三次並間隔 sleep(1)。

Socket 程式設計是系統級開發的入場券。掌握 API 語義、TCP 狀態機、位元組序、併發模型,才能寫出健壯、高效的網路軟體。

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