Linux 強大的網路能力源於 Socket 介面。本文從客戶端/伺服器架構、TCP/UDP 差異、常用除錯指令 (netstat, telnet) 切入,系統講解核心 Socket API:socket、bind、listen、accept、connect、位元組序轉換 (htonl/ntohl)、IP/域名解析 (gethostbyname)、inet_aton/ntoa,並提供可編譯執行的最簡 TCP Echo Server/Client 完整程式碼與 Makefile,帶您跨入網路程式設計大門。
socket → bind → listen → accept 循環等待連線。socket → connect 主動發起連線。int socket(int domain, int type, int protocol);
// domain: AF_INET (IPv4), AF_INET6, AF_UNIX (本機)
// type: SOCK_STREAM (TCP), SOCK_DGRAM (UDP)
// protocol: 通常 0
int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen);
// 綁定本地 IP:port,伺服器用 INADDR_ANY 監聽所有介面
int listen(int sockfd, int backlog); // 將 socket 轉為被動監聽,backlog 為佇列長度
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
// 阻塞等待連線,成功回傳新的 connected socket fd
int connect(int sockfd, struct sockaddr *addr, socklen_t addrlen);
// 客戶端主動連線指定 IP:port
htonl()/htons() // Host to Network Long/Short
ntohl()/ntohs() // Network to Host Long/Short
struct hostent *gethostbyname(const char *hostname); // 域名 → IP 列表
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type); // IP → 域名
char *inet_ntoa(struct in_addr in); // 32-bit IP → "a.b.c.d" 字串
int inet_aton(const char *cp, struct in_addr *inp); // "a.b.c.d" → 32-bit IP
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[]) {
if (argc != 2) { fprintf(stderr, "Usage: %s port\n", argv[0]); exit(1); }
int port = atoi(argv[1]);
if (port <= 0) { fprintf(stderr, "Invalid port\n"); exit(1); }
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) { perror("socket"); exit(1); }
struct sockaddr_in serv_addr = {0};
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("bind"); exit(1);
}
if (listen(sockfd, 5) < 0) { perror("listen"); exit(1); }
printf("Server listening on port %d...\n", port);
while (1) {
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);
int connfd = accept(sockfd, (struct sockaddr*)&cli_addr, &cli_len);
if (connfd < 0) { perror("accept"); continue; }
printf("Client connected: %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));
char *msg = "Hello from server!\n";
write(connfd, msg, strlen(msg));
close(connfd);
}
close(sockfd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[]) {
if (argc != 3) { fprintf(stderr, "Usage: %s hostname port\n", argv[0]); exit(1); }
char *host = argv[1];
int port = atoi(argv[2]);
struct hostent *he = gethostbyname(host);
if (!he) { fprintf(stderr, "Unknown host %s\n", host); exit(1); }
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr = {0};
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(port);
memcpy(&serv_addr.sin_addr, he->h_addr_list[0], he->h_length);
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("connect"); exit(1);
}
char buf[1024];
int n = read(sockfd, buf, sizeof(buf)-1);
if (n > 0) { buf[n] = '\0'; printf("Received: %s", buf); }
close(sockfd);
return 0;
}
all: server client
server: server.c
gcc -o server server.c
client: client.c
gcc -o client client.c
clean:
rm -f server client
make
./server 8888 & # 背景執行伺服器
./client localhost 8888
# 或用 telnet localhost 8888 測試
# 用 netstat -an | grep 8888 觀察連線狀態
netstat -an:顯示所有監聽/連線狀態。telnet host port:手動測試 TCP 連線、發送指令。tcpdump -i eth0 port 8888:封包擷取分析。掌握 Socket 基礎,即可構建各類網路服務、即時通訊、分散式系統的基石。