基於 BSD Socket API 實現 TCP 通信是網絡編程入門必修課。本文提供可直接編譯運行的服務端/客戶端完整代碼,涵蓋錯誤處理、地址轉換、循環服務等生產級要素。
#include#include #include #include #include #include #include #include #include #include #define PORT 3333 #define BUFSIZE 1024 int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } /* 1. 創建 Socket */ int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { perror("socket"); exit(EXIT_FAILURE); } /* 2. 解析服務器地址 */ struct hostent *host = gethostbyname(argv[1]); if (!host) { herror("gethostbyname"); close(sockfd); exit(EXIT_FAILURE); } /* 3. 填充 sockaddr_in */ struct sockaddr_in server_addr = {0}; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr = *(struct in_addr *)host->h_addr_list[0]; /* 4. 發起連接 */ if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("connect"); close(sockfd); exit(EXIT_FAILURE); } printf("Connected to %s:%d\n", inet_ntoa(server_addr.sin_addr), PORT); /* 5. 發送數據 */ char buffer[BUFSIZE]; printf("Enter message: "); if (fgets(buffer, BUFSIZE, stdin)) { send(sockfd, buffer, strlen(buffer), 0); } /* 6. 關閉 */ close(sockfd); return 0; }
#include#include #include #include #include #include #include #include #include #define PORT 3333 #define BACKLOG 5 #define BUFSIZE 1024 int main() { /* 1. 創建監聽 Socket */ int listenfd = socket(AF_INET, SOCK_STREAM, 0); if (listenfd == -1) { perror("socket"); exit(EXIT_FAILURE); } /* 2. 設置地址復用,避免 TIME_WAIT 干擾重啟 */ int opt = 1; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); /* 3. 綁定地址 */ struct sockaddr_in server_addr = {0}; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 監聽所有網卡 server_addr.sin_port = htons(PORT); if (bind(listenfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) { perror("bind"); close(listenfd); exit(EXIT_FAILURE); } /* 4. 監聽 */ if (listen(listenfd, BACKLOG) == -1) { perror("listen"); close(listenfd); exit(EXIT_FAILURE); } printf("Server listening on port %d...\n", PORT); /* 5. 主循環接受連接 */ while (1) { struct sockaddr_in client_addr; socklen_t addrlen = sizeof(client_addr); int connfd = accept(listenfd, (struct sockaddr *)&client_addr, &addrlen); if (connfd == -1) { perror("accept"); continue; // 不退出主循環 } printf("Client connected: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); /* 6. 接收數據 */ char buffer[BUFSIZE]; ssize_t n = recv(connfd, buffer, BUFSIZE - 1, 0); if (n > 0) { buffer[n] = '\0'; printf("Received: %s\n", buffer); } else if (n == 0) { printf("Client closed connection\n"); } else { perror("recv"); } /* 7. 關閉本次連接 */ close(connfd); } close(listenfd); // 理論不可達 return 0; }
# 服務端 gcc server.c -o server ./server # 客戶端(另開終端) gcc client.c -o client ./client 127.0.0.1
| 函數 | 作用 | 關鍵參數 |
|---|---|---|
| socket | 創建通信端點 | AF_INET(IPv4), SOCK_STREAM(TCP) |
| bind | 綁定本地地址 | INADDR_ANY 監聽所有接口 |
| listen | 轉為被動監聽 | backlog 控制已完成連接隊列長度 |
| accept | 取出已完成連接 | 返回新 socket,原 socket 繼續監聽 |
| connect | 主動發起連接 | 阻塞直到三次握手完成 |
| send/recv | 數據收發 | flags=0 普通;MSG_NOSIGNAL 忽略 SIGPIPE |
| htons/htonl | 主機序轉網絡序(大端) | 端口、IPv4 地址必須轉換 |
fork() 多進程 / pthread 多線程 / select/poll/epoll I/O 多路復用SIGINT/SIGTERM,關閉監聽套接字,等待子進程/線程退出掌握 socket → bind → listen → accept 服務端五步驟與 socket → connect 客戶端兩步驟,並熟練處理錯誤碼、字節序、地址轉換,是 Linux C 網絡編程的基石。本文代碼經實測可直接在 Ubuntu/CentOS/macOS 上運行,建議作為項目模板擴展。