WeHelp
C 語言可以直接呼叫作業系統提供的 System Call 且速度極快,用來開發基礎系統程式非常適合。
  1. C 系統程式
  2. C 檔案寫入、讀取
  3. Process 程序管理
  4. Thread 執行緒管理
  5. IPC 程序間通訊
  6. PIPE 程序間通訊
  7. FIFO 程序間通訊
  8. 共享記憶體通訊
  9. Socket 程序間通訊
  10. TCP 網路連線
Socket 程序間通訊
## IPC Socket 的特性 Socket 是一種由作業系統核心管理的通訊端點,可讓不同程序交換資料。常見特性如下: - **程序間通訊(IPC)**:可讓同一台主機上的不同程序互相傳送資料。 - **支援多種通訊模式**: - `SOCK_STREAM`:串流式通訊,可靠、有順序,常用於類似 TCP 的通訊。 - `SOCK_DGRAM`:資料報通訊,以一個封包為單位,不保證可靠送達。 - **支援本機與網路通訊**: - `AF_UNIX`/`AF_LOCAL`:同一台主機內的 IPC,透過檔案路徑識別 Socket。 - `AF_INET`:IPv4 網路通訊。 - `AF_INET6`:IPv6 網路通訊。 - **核心提供緩衝區與同步機制**:程序可使用 `read()`、`write()`、`send()`、`recv()` 傳送或接收資料。 - **可透過檔案權限控管**:Unix Domain Socket 通常會對應到檔案,因此可利用檔案權限限制存取。 --- ## C 如何透過 System Call 進行 Socket 通訊 典型的伺服器流程如下: ```text socket() ↓ bind() ↓ listen() ↓ accept() ↓ recv()/read() send()/write() ↓ close() ``` 用戶端流程如下: ```text socket() ↓ connect() ↓ send()/write() recv()/read() ↓ close() ``` 這些函式通常由 C 函式庫提供介面,但底層會呼叫核心的 System Call,例如: - `socket()`:建立 Socket - `bind()`:指定本地位址,例如 Unix Socket 路徑 - `listen()`:讓串流 Socket 等待連線 - `accept()`:接受用戶端連線 - `connect()`:連線至伺服器 - `send()`/`write()`:傳送資料 - `recv()`/`read()`:接收資料 - `close()`:關閉 Socket --- ## 簡單範例:Unix Domain Socket 以下程式可同時作為伺服器或用戶端,使用 `AF_UNIX` 在同一台主機上的兩個程序間傳送訊息。 ### `ipc_socket.c` ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/un.h> #define SOCKET_PATH "/tmp/demo_ipc.sock" void error_exit(const char *msg) { perror(msg); exit(EXIT_FAILURE); } void run_server(void) { int server_fd, client_fd; struct sockaddr_un addr; char buffer[128]; server_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (server_fd == -1) error_exit("socket"); unlink(SOCKET_PATH); // 移除可能存在的舊 Socket 檔案 memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1); if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) error_exit("bind"); if (listen(server_fd, 5) == -1) error_exit("listen"); printf("Server waiting...\n"); client_fd = accept(server_fd, NULL, NULL); if (client_fd == -1) error_exit("accept"); ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0); if (n == -1) error_exit("recv"); buffer[n] = '\0'; printf("Server received: %s\n", buffer); const char *reply = "Hello from server"; send(client_fd, reply, strlen(reply), 0); close(client_fd); close(server_fd); unlink(SOCKET_PATH); } void run_client(void) { int client_fd; struct sockaddr_un addr; char buffer[128]; client_fd = socket(AF_UNIX, SOCK_STREAM, 0); if (client_fd == -1) error_exit("socket"); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1); if (connect(client_fd, (struct sockaddr *)&addr, sizeof(addr)) == -1) error_exit("connect"); const char *message = "Hello from client"; send(client_fd, message, strlen(message), 0); ssize_t n = recv(client_fd, buffer, sizeof(buffer) - 1, 0); if (n == -1) error_exit("recv"); buffer[n] = '\0'; printf("Client received: %s\n", buffer); close(client_fd); } int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s server|client\n", argv[0]); return EXIT_FAILURE; } if (strcmp(argv[1], "server") == 0) run_server(); else if (strcmp(argv[1], "client") == 0) run_client(); else { fprintf(stderr, "Argument must be server or client\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } ``` ### 編譯 ```bash gcc ipc_socket.c -o ipc_socket ``` ### 執行 先開啟伺服器: ```bash ./ipc_socket server ``` 再開啟另一個終端機執行用戶端: ```bash ./ipc_socket client ``` 可能輸出: ```text Server received: Hello from client Client received: Hello from server ``` 此範例使用: - `AF_UNIX`:表示同一台主機內的 Unix Domain Socket。 - `SOCK_STREAM`:表示可靠且有順序的串流通訊。 - `bind()`:伺服器建立 `/tmp/demo_ipc.sock`。 - `connect()`:用戶端連線至該 Socket。 - `send()`/`recv()`:雙方交換資料。 若要改成網路通訊,通常會改用 `AF_INET`,並使用 IP 位址與 Port 作為 Socket 位址。
相關學習地圖、教學課程
C 語言,系統程式
掌握基礎的 C 語法,以及進階的系統程式開發能力。