WeHelp
C 語言可以直接呼叫作業系統提供的 System Call 且速度極快,用來開發基礎系統程式非常適合。
  1. C 系統程式
  2. C 檔案寫入、讀取
  3. Process 程序管理
  4. Thread 執行緒管理
  5. IPC 程序間通訊
  6. PIPE 程序間通訊
  7. FIFO 程序間通訊
  8. 共享記憶體通訊
  9. Socket 程序間通訊
  10. TCP 網路連線
TCP 網路連線
## TCP 通訊協定的主要特性 TCP(Transmission Control Protocol)位於傳輸層,主要特性如下: 1. **連線導向(connection-oriented)** 傳送資料前,必須先建立連線。通常透過三向交握(three-way handshake)建立 TCP 連線。 2. **可靠傳輸** TCP 使用序號、確認訊息(ACK)與逾時重傳,確保資料能夠可靠抵達。 3. **按順序傳遞** 即使資料封包經由不同路徑抵達,TCP 仍會依序交給應用程式。 4. **雙向傳輸(full duplex)** 連線兩端可以同時傳送與接收資料。 5. **流量控制與壅塞控制** TCP 會根據接收端處理能力及網路狀況調整傳輸速度,避免接收端緩衝區溢位或網路壅塞。 6. **位元組串流(byte stream)** TCP 不保留應用程式每次 `send()` 的訊息邊界。例如,兩次 `send()` 的資料可能被一次 `recv()` 讀取;一次 `send()` 的資料也可能需要多次 `recv()` 才能讀完。 --- ## C 如何透過 System Call 使用 TCP 在 Linux/Unix 中,C 程式通常透過 socket API 使用 TCP。這些函式大多是對核心 System Call 的包裝: ### TCP Server 常見流程 ```text socket() ↓ bind() ↓ listen() ↓ accept() ↓ recv() / send() ↓ close() ``` - `socket()`:建立 socket。 - `bind()`:指定本機 IP 位址與 Port。 - `listen()`:讓 socket 進入等待連線狀態。 - `accept()`:接受客戶端連線,並產生一個新的已連線 socket。 - `recv()` / `send()`:接收或傳送資料。 - `close()`:關閉連線。 ### TCP Client 常見流程 ```text socket() ↓ connect() ↓ send() / recv() ↓ close() ``` - `connect()`:連線到伺服器,核心會執行 TCP 三向交握。 - `send()`:將使用者空間的資料交給核心 TCP/IP 協定堆疊。 - TCP 核心負責分段、加上序號、重傳、確認及重新組合。 - `recv()`:從核心接收已按照順序組合好的位元組資料。 --- ## 簡單範例:TCP Echo/回覆程式 此範例中: - Server 監聽 TCP Port `8080` - Client 連線後傳送 `"Hello TCP"` - Server 收到後顯示資料並回傳 `"收到資料"` ### Server:`server.c` ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define PORT 8080 void error_exit(const char *msg) { perror(msg); exit(EXIT_FAILURE); } int main(void) { int listen_fd, client_fd; struct sockaddr_in server_addr; char buffer[1024]; /* 建立 TCP socket */ listen_fd = socket(AF_INET, SOCK_STREAM, 0); if (listen_fd < 0) error_exit("socket"); memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(PORT); /* 綁定 IP 與 Port */ if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) error_exit("bind"); /* 開始監聽 */ if (listen(listen_fd, 5) < 0) error_exit("listen"); printf("Server listening on port %d...\n", PORT); /* 接受一個客戶端連線 */ client_fd = accept(listen_fd, NULL, NULL); if (client_fd < 0) error_exit("accept"); /* 接收資料 */ ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0); if (n < 0) error_exit("recv"); buffer[n] = '\0'; printf("Server received: %s\n", buffer); /* 回覆資料 */ const char reply[] = "收到資料"; if (send(client_fd, reply, strlen(reply), 0) < 0) error_exit("send"); close(client_fd); close(listen_fd); return 0; } ``` ### Client:`client.c` ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #define PORT 8080 void error_exit(const char *msg) { perror(msg); exit(EXIT_FAILURE); } int main(void) { int sock_fd; struct sockaddr_in server_addr; char buffer[1024]; /* 建立 TCP socket */ sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (sock_fd < 0) error_exit("socket"); memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); /* 設定伺服器位址:本機 127.0.0.1 */ if (inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr) <= 0) error_exit("inet_pton"); /* 連線到 Server */ if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) error_exit("connect"); /* 傳送資料 */ const char message[] = "Hello TCP"; if (send(sock_fd, message, strlen(message), 0) < 0) error_exit("send"); /* 接收 Server 回覆 */ ssize_t n = recv(sock_fd, buffer, sizeof(buffer) - 1, 0); if (n < 0) error_exit("recv"); buffer[n] = '\0'; printf("Client received: %s\n", buffer); close(sock_fd); return 0; } ``` ### 編譯與執行 開啟兩個終端機: ```bash gcc server.c -o server gcc client.c -o client ``` 先執行 Server: ```bash ./server ``` 再執行 Client: ```bash ./client ``` 預期結果: ```text Server received: Hello TCP Client received: 收到資料 ``` 在這個過程中,`connect()` 會觸發 TCP 連線建立;`send()` 將資料交給核心的 TCP/IP 協定堆疊;TCP 負責封裝、傳送、確認與必要的重傳;對端的 `recv()` 則取得可靠且依序排列的資料。實際程式中還需注意 `send()` 和 `recv()` 可能只處理部分資料,因此傳送大型資料時通常要以迴圈確保全部資料都被處理。
相關學習地圖、教學課程
C 語言,系統程式
掌握基礎的 C 語法,以及進階的系統程式開發能力。