Windows XP Windows 7 Windows 2003 Windows Vista Windows 綜合教學 Linux 系統教學
Windows 10 Windows 8 Windows 2008 Windows NT Windows Server 電腦軟體教學
您现在的位置: Windows教程網 >> Windows教程 >  >> Linux 系統教學 >> Linux 教學

Unix 網路程式設計入門:Socket API 完整教學

編輯:Linux 教學

前言

Socket 是 Unix/Linux 網路通訊的核心 API,支援 TCP (可靠串流)、UDP (資料報) 等協定。本文系統介紹 Socket 概念、系統調用、TCP/UDP 客戶端/伺服器模型與完整程式範例。

1. Socket 類型

類型協定特性適用場景
SOCK_STREAMTCP面向連接、可靠、有序、無邊界、全雙工Web、SSH、資料庫、檔案傳輸
SOCK_DGRAMUDP無連接、不可靠、無序、有邊界 (訊息導向)DNS、串流媒體、即時遊戲、廣播
SOCK_RAWIP/ICMP/自定義直接存取網路層,需 root 權限路由協定、防火牆、封包分析

2. 核心系統調用

#include <sys/socket.h>

int socket(int domain, int type, int protocol);      // 建立 socket,回傳 fd
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // 綁定地址
int listen(int sockfd, int backlog);                 // 監聽 (僅 TCP)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);   // 接受連接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); // 發起連接

ssize_t read(int fd, void *buf, size_t count);       // 讀取 (TCP)
ssize_t write(int fd, const void *buf, size_t count); // 寫入 (TCP)
ssize_t recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *src, socklen_t *addrlen); // UDP 接收
ssize_t sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *dest, socklen_t addrlen); // UDP 發送
int close(int fd);                                   // 關閉

3. TCP 客戶端/伺服器流程

伺服器端

  1. socket() 建立 AF_INET, SOCK_STREAM。
  2. bind() 綁定 INADDR_ANY (所有介面) 與埠號。
  3. listen() 設置等待隊列長度 (backlog)。
  4. 迴圈 accept() 阻塞等待連接,回傳新 connfd。
  5. 在新 connfd 上 read()/write() 通訊。
  6. close(connfd) 結束該連接,繼續 accept()。

客戶端

  1. socket() 建立同類型 socket。
  2. connect() 連接伺服器 IP:Port。
  3. read()/write() 通訊。
  4. close() 結束。

4. UDP 客戶端/伺服器流程

  • 雙方皆 socket(AF_INET, SOCK_DGRAM) 並 bind() (客戶端可讓核心自動分配臨時埠)。
  • 無 listen/accept/connect (可選用 connect() 固定對端簡化收發)。
  • 伺服器 recvfrom() 取得資料與來源地址 → 處理 → sendto() 回覆。
  • 客戶端 sendto() 發送請求 → recvfrom() 等待回覆。

5. 完整範例:TCP 回傳遞增序列數

server.c

#include <stdio.h> #include <stdlib.h> #include <string.h>
#include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h>

#define PORT 1032
#define BACKLOG 5

int main() {
    int listenfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serv = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_ANY), .sin_port = htons(PORT) };
    bind(listenfd, (struct sockaddr*)&serv, sizeof(serv));
    listen(listenfd, BACKLOG);

    printf("Server listening on port %d...\n", PORT);
    while (1) {
        struct sockaddr_in cli; socklen_t clilen = sizeof(cli);
        int connfd = accept(listenfd, (struct sockaddr*)&cli, &clilen);
        printf("Client connected: %s:%d\n", inet_ntoa(cli.sin_addr), ntohs(cli.sin_port));

        uint32_t seq = 0;
        while (1) {
            uint32_t net_seq = htonl(seq++);
            if (write(connfd, &net_seq, 4) != 4) break; // 客戶端斷開
            usleep(10000); // 模擬間隔
        }
        close(connfd);
        printf("Client disconnected.\n");
    }
    close(listenfd);
    return 0;
}

client.c

#include <stdio.h> #include <stdlib.h> #include <string.h>
#include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h>

#define PORT 1032

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 serv = { .sin_family = AF_INET, .sin_port = htons(PORT) };
    inet_pton(AF_INET, argv[1], &serv.sin_addr);

    connect(sockfd, (struct sockaddr*)&serv, sizeof(serv));
    printf("Connected to %s:%d\n", argv[1], PORT);

    uint32_t expected = 0, recv_seq;
    int dots = 0;
    while (read(sockfd, &recv_seq, 4) == 4) {
        recv_seq = ntohl(recv_seq);
        if (recv_seq != expected) {
            fprintf(stderr, "\nError: expected %u, got %u\n", expected, recv_seq);
            expected = recv_seq;
        } else {
            expected++;
        }
        if (++dots % 50 == 0) { putchar('.'); fflush(stdout); }
        if (dots % 1000 == 0) putchar('\n');
    }
    close(sockfd);
    return 0;
}

編譯執行

gcc server.c -o server
gcc client.c -o client
./server          # 終端 1
./client 127.0.0.1  # 終端 2

6. 進階主題

  • I/O 多路複用:select/poll/epoll 處理高併發 (C10K 問題)。
  • 非阻塞 I/O:fcntl(fd, F_SETFL, O_NONBLOCK) 配合 epoll (ET 模式)。
  • 訊息邊界處理:TCP 為串流無邊界,應用層需自行定義協議 (長度前綴、分隔符、固定長度)。
  • Socket 選項:SO_REUSEADDR (快速重啟)、TCP_NODELAY (關閉 Nagle)、SO_RCVBUF/SO_SNDBUF (緩衝區大小)。
  • 跨平台:Windows 使用 Winsock (WSAStartup, SOCKET 型別, closesocket),邏輯相同。

結語

Socket API 是網路程式設計的基石。掌握 TCP/UDP 生命週期、正確處理錯誤碼 (EINTR, EAGAIN, ECONNRESET) 與 I/O 多路複用,是構建高效能網路服務的關鍵。

Copyright © Windows教程網 All Rights Reserved