Socket 是 Unix/Linux 網路通訊的核心 API,支援 TCP (可靠串流)、UDP (資料報) 等協定。本文系統介紹 Socket 概念、系統調用、TCP/UDP 客戶端/伺服器模型與完整程式範例。
| 類型 | 協定 | 特性 | 適用場景 |
|---|---|---|---|
SOCK_STREAM | TCP | 面向連接、可靠、有序、無邊界、全雙工 | Web、SSH、資料庫、檔案傳輸 |
SOCK_DGRAM | UDP | 無連接、不可靠、無序、有邊界 (訊息導向) | DNS、串流媒體、即時遊戲、廣播 |
SOCK_RAW | IP/ICMP/自定義 | 直接存取網路層,需 root 權限 | 路由協定、防火牆、封包分析 |
#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); // 關閉
socket() 建立 AF_INET, SOCK_STREAM。bind() 綁定 INADDR_ANY (所有介面) 與埠號。listen() 設置等待隊列長度 (backlog)。accept() 阻塞等待連接,回傳新 connfd。connfd 上 read()/write() 通訊。close(connfd) 結束該連接,繼續 accept()。socket() 建立同類型 socket。connect() 連接伺服器 IP:Port。read()/write() 通訊。close() 結束。socket(AF_INET, SOCK_DGRAM) 並 bind() (客戶端可讓核心自動分配臨時埠)。listen/accept/connect (可選用 connect() 固定對端簡化收發)。recvfrom() 取得資料與來源地址 → 處理 → sendto() 回覆。sendto() 發送請求 → recvfrom() 等待回覆。#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;
}
#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
select/poll/epoll 處理高併發 (C10K 問題)。fcntl(fd, F_SETFL, O_NONBLOCK) 配合 epoll (ET 模式)。SO_REUSEADDR (快速重啟)、TCP_NODELAY (關閉 Nagle)、SO_RCVBUF/SO_SNDBUF (緩衝區大小)。WSAStartup, SOCKET 型別, closesocket),邏輯相同。Socket API 是網路程式設計的基石。掌握 TCP/UDP 生命週期、正確處理錯誤碼 (EINTR, EAGAIN, ECONNRESET) 與 I/O 多路複用,是構建高效能網路服務的關鍵。