連網(NETWORKING)

前言大綱

所有應用程序都通過網絡連接與 Spot 的 API 對話。了解網絡協議對於調試問題和創建強大的高性能應用程序至關重要。

網絡的選擇

Spot 提供了多種網絡選項來支持多種應用程序和環境。選項包括:

  • 作為連接的對等點。應用程序可以部署在通過後 RJ-45 端口、DB-25 外掛負載端口或 Spot GXP  外掛負載上的 RJ-45 端口實體連接到 Spot 的計算機上。這提供了沒有基礎設施要求的可靠、高速率的通信鏈接,但限制了應用程序可以運行的位置。
  • Spot作為 WiFi 接入點。與 Spot 實體接近的應用程序可以連接到 WiFi 接入點並直接通信,無需任何網絡基礎設施。
  • Spot 作為 WiFi 客戶端。 Spot 可以加入現有的 WiFi 網絡,應用程序也可以加入同一個 WiFi 網絡與 Spot 通話。這種方法增加了應用程序和 Spot 之間的可能範圍,但需要注意網絡中的死區、接入點之間的切換時間以及其他考慮因素。
  • 通過自定義通信鏈接。諸如蜂巢是數據機和持久系統無線電之類的自定義通信鏈接可以充當 Spot 和應用程序之間的橋樑。當上述情況不足以進行網絡設計時,這些會很有用。
雖然 Spot API 的應用層協議適用於任何基於 IP 的網絡連接,但以上示例表明網絡選擇會對應用程序的性能和可靠性以及部署策略產生重大影響。

gRPC 和協議緩衝區(gRPC and Protocol Buffers)

gRPC 是大多數 Spot API 使用的應用程序級協議。選擇 gRPC 是因為它提供了一種安全、高性能的協議,並支持廣泛的編程語言和環境。

gRPC 指定了服務支持的服務接口和遠端程序呼叫 (RPC: Remote Procedure Call)。例如,Spot API 有一個身份驗證服務,定義如下:

service AuthService {
    rpc GetAuthToken(GetAuthTokenRequest) returns (GetAuthTokenResponse) {}
}

服務接口的名稱是 AuthService。它只支持名為 GetAuthToken 的單個 RPC,它檢查傳入的用戶名和密碼,如果它們有效則返回會話令牌(token)。

輸入 (GetAuthTokenRequest) 和輸出 (GetAuthTokenResponse) 消息是協議緩衝區(Protocol Buffer)消息。協議緩衝區消息具有緊湊的連線表示,支持向後/向前兼容性,並為廣泛的編程語言和環境實現。

協議緩衝區消息使用獨立於語言的格式定義,並為語言自動生成序列化/反序列化綁定。例如,可以在下面(或在 auth.proto 中)看到 GetAuthTokenRequest 的定義:

message GetAuthTokenRequest {
    // Common request header.
    RequestHeader header = 1;
    // Username to authenticate with. Must be set if password is set.
    string username = 2;
    // Password to authenticate with. Not necessary if token is set.
    string password = 3;
    // Token to authenticate with. Can be used in place of the password, to re-mint a token.
    string token = 4;
}

儘管 AuthService 示例展示了單請求/單響應的通用 RPC 範式,但 gRPC 還支持更高級的 RPC 範式,例如串流式請求、串流式響應或完全雙向串流式傳輸。Spot API 在選定位置使用一些更高級的範例。例如,gRPC 默認將響應限制為 4MB,這對於大量數據(例如完整的 GraphNav 地圖)來說太小了。GraphNav 服務的上傳和下載請求提供了一個串流響應 RPC 來解決這個限制。

使用 gRPC 和協議緩衝區(Using gRPC and Protocol Buffers)

SDK 中包含的 Python 程序庫隱藏了 gRPC 和協議緩衝區的使用,並提供了更簡單的抽象。 例如,與上述 AuthService 接口對話的 AuthClient 類有一個稱為 auth 的方法,如下所示。

def auth(self, username, password, app_token=None, **kwargs):
        """Authenticate to the robot with a username/password combo.

        Args:
            username: username on the robot.
            password: password for the username on the robot.
            app_token: Deprecated.  Only include for robots with old software.
            kwargs: extra arguments for controlling RPC details.

        Returns:
            User token from the server as a string.

        Raises:
            InvalidLoginError: If username and/or password are not valid.
        """
        req = _build_auth_request(username, password, app_token)
        return self.call(self._stub.GetAuthToken, req, _token_from_response, _error_from_response,
                         **kwargs)

此方法獲取用戶名(username)、密碼(password)和 app_token 字符串,並從使用 _build_auth_request 的那些字符串中生成 GetAuthTokenRequest 消息。然後將 RPC 發送到 AuthService 並接收 GetAuthTokenResponse。如果身份驗證嘗試有效,會話令牌將返回給身份驗證函數的調用者。如果存在網絡問題或用戶名/密碼組合無效,則會引發異常。

這一抽象層簡化了常見用例,但開發人員偶爾需要直接與協議緩衝區消息交互,即使在使用 Python 函式庫時也是如此。例如,RobotState 消息包括許多階層巢狀式的子消息和詳細信息。開發人員不需要轉換成原始類型或其他數據結構,而是需要直接對協議緩衝區本身進行操作。protos/bosdyn/api/robot_state.proto 中的消息定義和註釋是理解包含的內容以及如何解釋數據的關鍵。

希望支持其他語言或環境的開發人員也可以使用 gRPC 服務定義和協議緩衝區消息定義構建客戶端函式庫。Python 客戶端函式庫也可用作參考實現。

錯誤處理(Error Handling)

機器人的應用程序在與 Spot API 通信時需要處理錯誤。常見的錯誤分為三類:

  • 網絡錯誤。範例:網絡超時、無法訪問 Spot 或安全問題。解決方法通常是特定於網絡配置的。
  • 常見的 RPC 錯誤。範例:無效輸入或內部服務器錯誤。通常根本原因是錯誤的應用程序代碼,唯一的解決方法是修復錯誤。
  • 特定的 RPC 錯誤。範例:基於返回的狀態(Status) 消息的服務特定錯誤,例如上述 AuthService 的錯誤用 戶名/密碼 組合。應用程序代碼應該優雅地處理這些錯誤。

更多細節可以在下面的 GetAuthTokenResponse 協議緩衝區消息中看到:

message GetAuthTokenResponse {
    ResponseHeader header = 1;
    enum Status{
        // STATUS_UNKNOWN should never be used. If used, an internal error has happened.
        STATUS_UNKNOWN = 0;

        // STATUS_OK indicates that authentication has succeeded. The |token| field will
        // be populated with a session token that can be used to authenticate the user.
        STATUS_OK = 1;

        // STATUS_INVALID_LOGIN indicates that authentication has failed since an invalid
        // username and/or password were provided.
        STATUS_INVALID_LOGIN = 2;

        // STATUS_INVALID_TOKEN indicates that authentication has failed since the |token|
        // provided in the request is invalid. Reasons for the token being invalid could be
        // because it has expired, because it is improperly formed, for the wrong robot, the
        // user that the token is for has changed a password, or many other reasons. Clients
        // should use username/password-based authentication when refreshing the token fails.
        STATUS_INVALID_TOKEN = 3;

        // STATUS_TEMPORARILY_LOCKED_OUT indicates that authentication has failed since
        // authentication for the user is temporarily locked out because of too many unsuccessful
        // attempts. Any new authentication attempts should be delayed so they may happen after
        // the lock out period ends.
        STATUS_TEMPORARILY_LOCKED_OUT = 4;

        // STATUS_INVALID_APPLICATION_TOKEN indicates that the |application_token| in the
        // request was invalid.
        STATUS_INVALID_APPLICATION_TOKEN = 5;

        // STATUS_EXPIRED_APPLICATION_TOKEN indicates that the |application_token| in the
        // request was valid, but has expired.
        STATUS_EXPIRED_APPLICATION_TOKEN = 6;
    }
    Status status = 2;

    // Token data. Only specified if status == STATUS_OK.
    string token = 3;
}

網絡錯誤未在消息中定義,而是發生在 gRPC 堆棧的較低位置。特定 RPC 錯誤由上面顯示的狀態(Status) 枚舉處理。在整個 API 中,狀態(Status) 枚舉遵循以下模式:

  • 0 未定義(由於協議緩衝區表示未設置數據的方式),
  • 1 表示 OK 或成功響應,
  • 2+ 表示特定於 RPC 的錯誤代碼。
例如,STATUS_TEMPORARILY_LOCKED_OUT 用於如果用戶嘗試密碼的次數過多並且帳戶被鎖定了很短的時間。

上面沒有直接顯示無效輸入等常見的 RPC 錯誤。無效輸入的一種情況是 GetAuthTokenRequest 根本不包含用戶名或密碼,這表明有錯誤的客戶端代碼而不是錯誤的用戶輸入。常見的 RPC 錯誤顯示在 ResponseHeader 消息中,其下方的所有 Spot API 響應包括:

/// Standard header attached to all GRPC responses from services.
message ResponseHeader {
    /// Echo-back the RequestHeader for timing information, etc....
    RequestHeader request_header = 1;

    /// Time that the request was received. The server clock is the time basis.
    google.protobuf.Timestamp request_received_timestamp = 2;

    /// Time that the response was received. The server clock is the time basis.
    google.protobuf.Timestamp response_timestamp = 3;

    /// Common errors, such as invalid input or internal server problems.
    /// If there is a common error, the rest of the response message outside of the
    /// ResponseHeader will be invalid.
    CommonError error = 4;

    /// Echoed request message. In some cases it may not be present, or it may be a stripped
    /// down representation of the request.
    google.protobuf.Any request = 5;
}

程序開發人員可以接受,以Python 函式庫通過引發特定異常(Exceptions)來處理連網、常見 RPC 錯誤和特定 RPC 錯誤。

HTTP/2, TLS, and TCP

gRPC 建立在其他網絡協議之上,如下圖所示。

這個協議棧有幾個含義:

HTTP/2 支持通過同一網絡連接多個 gRPC Calls。 響應可以以與接收不同的順序返回,低優先級的響應可以被更高優先級的響應中斷,然後再恢復。 gRPC 請求直接映射到 HTTP/2 請求。

所有通信都通過安全、加密的 TLS 通道(支持 TLS1.2 或 TLS1.3)。網絡攻擊者無法讀取或操作應用程序和 Spot 之間的數據。客戶端函式庫還應驗證服務器提供的證書是否鏈接到波士頓動力根證書,以防止主動中間人攻擊MITM (Man-in-the-middle)。Python 客戶端函式庫會自動執行此證書驗證方法。

通信是通過 TCP 的可靠傳輸層進行的。可靠傳輸對於非實時 RPC 是一種很好的方法,例如對 Spot 進行身份驗證,並且對於存在強大網絡鏈接(例如通過 RJ45 或 DB-25 連接器)的實時情況也沒有問題。但是,在不穩定的網絡連接上處理實時 RPC 時可能會出現問題。有一些短期緩解方法可以解決此問題,例如 : 維護套接字池以循環請求( maintaining a socket pool to round-robin requests over.)。從長遠來看,Boston Dynamics 正在探索其他方法(例如基於 QUIC 或 HTTP/3 的 gRPC)來改善不良網絡鏈接上的通信。

協議棧是整個 Internet 中非常常見的協議棧。 這有兩個好處。 

  1. 首先,Spot 能夠使用經過實戰測試的協議實現,可以抵禦對抗性攻擊者。 
  2. 其次,應用程序極有可能通過各種網絡訪問 Spot,而不會被中間防火牆阻止。

機器人發現

要與 Spot 通信,客戶端應用程序需要指定運行 Spot 的 IP 地址。進行這種機器人探索有多種可能的選擇。

  • 固定IP地址。當直接作為 WiFi 接入點或通過以太網連接到 Spot 時,可以可靠地使用這種方法。不需要名稱查找基礎結構。
  • DNS 名稱。可以將 DNS 服務器(或 HOSTS 文件)配置為靜態指向 Spot 偵聽的固定 IP 地址,並且應用程序指定要訪問的 DNS 名稱。
  • mDNS 發現。從 Spot 2.0 版本開始,Spot 將發送 mDNS/DNS-SD 廣播。同一網絡上的客戶端可以偵聽這些數據包並通過公告發現可用的機器人。
  • 自定義發現機制。應用程序可以開發自定義方法來將特定 Spot 映射到 IP 地址,例如使用基於雲的發現端點。

在與機器人的 TLS 握手交談期間驗證證書時,客戶端應用程序不使用機器人的符號名稱(如果有)。這支持多種發現機制,而無需提供新證書。


參考資料

特色、摘要,Feature、Summary:

關鍵字、標籤,Keyword、Tag:

留言

這個網誌中的熱門文章

Ubuntu 常用指令、分類與簡介

iptables的觀念與使用

網路設定必要參數IP、netmask(遮罩)、Gateway(閘道)、DNS

了解、分析登錄檔 - log

Python 與SQLite 資料庫

Blogger文章排版範本

Pandas 模組

如何撰寫Shell Script

查詢指令或設定 -Linux 線上手冊 - man

下載網頁使用 requests 模組