網路與通訊端

簡單的回聲客戶端與伺服器(a simple echo client and server)

每一門手藝都有它第一個把每樣基本工具各用一次的簡單作品——在網路裡,那就是回聲伺服器。伺服器的全部工作就是聆聽某個客戶端、讀取客戶端送來的任何位元組,再把那些相同的位元組原封送回去。客戶端連上、送一行字,再印出回傳的內容。若你的回聲配對能動,你就正確地把整套通訊端 API 完整演練了一遍,這正是為什麼它是網路程式設計的「hello world」。

把各部分拼起來:伺服器呼叫 socket()、bind() 到一個連接埠、listen(),然後迴圈呼叫 accept() 替每位客戶端取得一個已連線通訊端;對每位客戶端,它迴圈 recv() 讀位元組、send() 把相同位元組寫回去,直到 recv() 回傳 0(客戶端關閉了),再 close() 那個已連線通訊端。客戶端呼叫 socket()、connect() 到伺服器位址(常是某個選定連接埠上的 127.0.0.1),然後 send() 一些資料、recv() 回覆,若回覆分段抵達就迴圈。關鍵在於:即使這麼小的程式也必須對 send() 與 recv() 迴圈,而非假設一次呼叫就搬完一切,因為 TCP 是一道沒有訊息邊界的位元組串流——回聲伺服器單純地把收到的轉發回去,正好避開了封幀,這也是它之所以是自然的第一個範例的部分原因。

為何重要:回聲配對是最小的、同時觸及 socket()、bind()、listen()、accept()、connect()、send()、recv() 與 close() 的完整作品,外加連接埠的位元組序轉換、以及 recv() 回傳 0 的關閉訊號。把它做一次、讓它在迴路上跑起來,那些抽象的呼叫就變得具體。從這裡開始,一個聊天伺服器、一個網頁伺服器、或任何協定,都是同一副骨架疊上更豐富的規則——這正是後面的領域與第二卷所探索的內容。

伺服器內層迴圈:for (;;) { ssize_t n = recv(c, buf, sizeof buf, 0); if (n <= 0) break; /* 0 = 客戶端關閉,<0 = 錯誤 */ size_t off = 0; while (off < (size_t)n) { ssize_t m = send(c, buf + off, n - off, 0); if (m < 0) break; off += m; } } 它讀一塊、再把它全部送回去,並用迴圈的 send() 處理部分寫入。

整個回聲伺服器:recv 一塊、迴圈 send() 直到全部送回,重複到客戶端關閉為止。

回聲伺服器藏起了封幀,因為它從不需要知道一則訊息在哪裡結束——它只轉發原始位元組。一旦你開始打造真正的協定,就必須自己加上封幀;別讓回聲範例騙你以為 TCP 會遞送訊息。

又稱
echo serverthe hello world of networking回聲伺服器回聲程式