Unix 套接字程序通訊初探【Go 版本】

FunTester發表於2024-05-27

最近看到一些資料,提到了在同一臺機器上程序間通訊的方式:unix 套接字。起初我還以為是 Socket 介面,因為用到了變數 SocketPath。後面經過 AI 助理翻譯,才知道原來這是我的知識盲區了。

現在趕緊把這塊知識補充上。

Unix 套接字簡介

Unix 套接字(Unix domain sockets)是一種用於同一主機上程序間通訊(IPC,Inter-Process Communication)的機制。與網路套接字不同,Unix 套接字不使用網路協議棧,因此效能更高。它們主要用於需要高效、低延遲的本地程序通訊場景。

Unix 套接字的型別

  1. 流套接字(SOCK_STREAM)
    • 提供面向連線的、可靠的位元組流服務,類似於 TCP。
    • 資料傳輸具有順序和可靠性保障。
  2. 資料包套接字(SOCK_DGRAM)
    • 提供無連線的、訊息為單位的資料傳輸,類似於 UDP。
    • 資料傳輸不保證順序和可靠性。

Unix 套接字的工作流程

伺服器端操作流程:

  1. 建立套接字:使用系統呼叫建立一個套接字檔案描述符。
  2. 繫結套接字:將套接字繫結到一個檔案系統路徑,類似於網路套接字繫結到 IP 地址和埠。
  3. 監聽連線:使套接字進入監聽狀態,準備接受客戶端連線。
  4. 接受連線:當有客戶端請求連線時,接受連線並建立一個新的套接字檔案描述符用於通訊。
  5. 通訊:透過讀寫操作在伺服器和客戶端之間傳輸資料。
  6. 關閉套接字:完成通訊後,關閉套接字並清理資源。

客戶端操作流程:

  1. 建立套接字:使用系統呼叫建立一個套接字檔案描述符。
  2. 連線到伺服器:使用系統呼叫連線到伺服器端的套接字路徑。
  3. 通訊:透過讀寫操作在客戶端和伺服器之間傳輸資料。
  4. 關閉套接字:完成通訊後,關閉套接字並清理資源。

優點和應用場景

優點:

  1. 高效:由於不涉及網路協議棧的處理,Unix 套接字具有更低的開銷和更高的效能。
  2. 安全:Unix 套接字只能在本地主機上使用,降低了網路攻擊的風險。
  3. 簡單:配置和使用比網路套接字更簡單,不需要考慮網路配置和防火牆等問題。

典型應用場景:

  1. 本地程序通訊:例如,系統服務之間或應用程式內部元件之間的通訊。
  2. 高效能服務:需要高效、低延遲的本地服務,例如資料庫服務(如 MySQL)。
  3. 容器間通訊:在容器化環境中,本地容器之間的通訊。

Unix 套接字是一種高效、可靠的本地程序間通訊機制,適用於需要低延遲和高效能的應用場景。它們透過檔案系統路徑進行標識和通訊,使用方便且配置簡單,是本地主機上程序通訊的重要工具。

Go 語言實現

下面來用 Go 語言實現一個基於 unix 的服務端和客戶端,並且模擬進行程序間的通訊。下期我們將會用 Java&Groovy 重新實現一遍。同時測試一下誇語音 unix 套接字程序間通訊。

服務端

// TestServer 測試服務端  
//  
//  @Description: 測試服務端  
//  @param t  
func TestServer(t *testing.T) {  
    if _, err := os.Stat(socketPath); err == nil {  
       os.Remove(socketPath) // 如果檔案存在,刪除檔案  
    }  
    listener, err := net.Listen("unix", socketPath) // 監聽,建立一個unix socket  
    if err != nil {  
       fmt.Println("監聽發生異常:", err)  
       return  
    }  
    defer listener.Close() // 關閉監聽  
    fmt.Println("服務啟動...")  
    for { // 迴圈  
       conn, err := listener.Accept() // 接收連線  
       if err != nil {  
          fmt.Println("接受資訊錯誤:", err)  
          return  
       }  
       go handleConnection(conn) // 處理連線  
    }  
}

其中處理連線的方法內容如下:

// handleConnection 處理連線  
//  
//  @Description: 處理連線  
//  @param conn  
func handleConnection(conn net.Conn) {  
    defer conn.Close()           // 關閉連線  
    buffer := make([]byte, 1024) // 建立一個緩衝區  
    n, err := conn.Read(buffer)  // 讀取資料  
    if err != nil {              // 如果讀取錯誤  
       fmt.Println("讀取錯誤:", err)  
       return  
    }  
    fmt.Printf("收到訊息: %s\n", string(buffer[:n])) // 列印接收到的資料  
}

客戶端

客戶端程式碼比較簡單,實現了傳送訊息的功能。

// TestClient 測試客戶端  
//  
//  @Description: 測試客戶端  
//  @param t  
func TestClient(t *testing.T) {  
    conn, err := net.Dial("unix", socketPath)  
    if err != nil {  
       fmt.Println("撥號發生錯誤:", err)  
       return  
    }  
    defer conn.Close()  
    message := "Hello FunTester"  
    _, err = conn.Write([]byte(message))  
    if err != nil {  
       fmt.Println("寫入訊息錯誤:", err)  
       return  
    }  
}

測試控制檯列印

經過多次傳送訊息,控制檯列印資訊如下:

服務啟動...
收到訊息: Hello FunTester
收到訊息: Hello FunTester
收到訊息: Hello FunTester

下期的 Java 和 Groovy 版本敬請期待!!!

  • 2021 年原創合集
  • 2022 年原創合集
  • 2023 年原創合集
  • 服務端功能測試
  • 效能測試專題
  • Java、Groovy、Go、Python
  • 單元&白盒&工具合集
  • 測試方案&BUG&爬蟲&UI 自動化
  • 測試理論雞湯
  • 社群風采&影片合集
如果覺得我的文章對您有用,請隨意打賞。您的支援將鼓勵我繼續創作!
打賞支援
暫無回覆。

相關文章