0 前言
在物聯網領域中,mqtt訊息一直是海量裝置連線到平臺的標配協議,而平臺向移動端開放的操作介面往往是http協議,這就要求平臺為兩種協議作訊息一一適配。在某些情況下,這些裝置是有作業系統的linux或安卓裝置,如果我們換個思路,讓這些裝置直接提供http協議的操作介面(httpd伺服器),平臺端使用隧道打通裝置與移動端的傳輸層,做為平臺開發人員,就可以省去時間做這些繁瑣的訊息級別的一一適配工作了。
1 面臨的問題
1.1 海量連線
mqtt預設基於tcp傳輸,一臺裝置與平臺維持一個tcp連線即可高效的上下並行傳輸訊息。而http/1.1在協議層限制了單個連線序列請求響應,但我們可以使用http/2.0或http/3.0做為http/1.1的傳輸層,也能達到一個物理連結多路傳輸http/1.1的目的。這樣下來內網http/1.1穿透也和mqtt一樣,只需要一個物理長連線。
1.2 身份認證
我嘗試使用frp來承載物聯網裝置的http內網穿透,但至今還沒成功完成海量裝置的一機一密的身份認證,它裡面的token
認證方式只適合一平臺對一個裝置的安全要求,而OIDC驗證我至今仍然看不明白怎麼使用。對於一款內網穿透元件或應用,能像asp.netcore的身份驗證強大,對平臺端而言是非常需要的。
1.3 安全傳輸
裝置內建的http伺服器,一般都是沒有https,因為它本身就不考慮公網傳輸的能力。但在內網穿透之後,我們必須要考慮裝置到公網平臺這段的傳輸安全。
1.4 開放協議
內網穿透元件涉及到平臺的服務端和整合在裝置裡的客戶端庫,平臺端一般只有二次開發的需求。而裝置端由於晶片、系統和指令集、記憶體限制等等因素,只提供客戶端庫或二進位制可執行檔案是不夠的,還可能需要客戶端開發者根據互動協議來自行開發客戶端元件。這就要求內網穿透元件提供客戶端與服務端的互動協議,且最好是設計為非常簡單的協議。
2 CYarp出場
CYarp基於Yarp的http內網穿透中介軟體,支援tcp、http/2.0或http/3.0作為http/1.1的傳輸層,其具有以下特徵
- 是一個標準的asp.netcore中介軟體
- 能完整使用asp.netcore框架和其它中介軟體
- 使用服務端的tls(https)做為安全傳輸層
- 單連線多路複用,無需客戶端提供http2伺服器
- 協議透明簡單,參考了WebSocket升級和Bootstrapping WebSockets with HTTP/2
2.1 服務端開發
CYarp中介軟體其依賴於Authentication身份認證中介軟體,使用如下方法進行註冊和中介軟體的配置。
builder.Services.AddCYarp(cyarp=>
{
...
});
中介軟體配置順序如下:
...
app.UseAuthentication();
...
app.UseCYarp();
...
最後在Controller、minapi的處理者或中介軟體中處理http轉發
// 請求者的授權驗證
[Authorize(Roles = "Mobile")]
public class CYarpController : ControllerBase
{
private static readonly string clientIdClaimType = "ClientId";
/// <summary>
/// 處理cyarp
/// 核心操作是從請求上下文獲取clientId
/// 然後使用clientId從clientManager獲取client來轉發http
/// </summary>
/// <param name="clientManager"></param>
/// <returns></returns>
[Route("/{**cyarp}")]
public async Task InvokeAsync([FromServices] IClientManager clientManager)
{
var clientId = this.User.FindFirstValue(clientIdClaimType);
if (clientId != null && clientManager.TryGetValue(clientId, out var client))
{
this.Request.Headers.Remove(HeaderNames.Authorization);
await client.ForwardHttpAsync(this.HttpContext);
}
else
{
this.Response.StatusCode = StatusCodes.Status502BadGateway;
}
}
}
2.2 客戶端開發
使用CYary.Client.CYarpClient
很方便完成客戶端開發
using var client = new CYarpClient();
while (true))
{
await client.TransportAsync(this.clientOptions.CurrentValue, stoppingToken).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
}
2.3 客戶端協議
由於篇幅有限,不在這裡展開,可以查閱CYarp握手協議
3 同源產品
Yarp是dotnet平臺下堪比nginx的一個元件,其它融入asp.netcore框架作為裡面的中介軟體共享asp.netcore生態。
davidfowl大神也曾經小手一揮造就了YarpTunnelDemo專案,也是實現了http內網穿透的能力。但是他的實現方案要求客戶端方能要執行asp.netcore監聽做為客戶端元件,由於asp.netcore的runtime只適配了桌面系統,且編譯出的二進位制檔案很大,此方法自然無法在物聯網裝置中執行了。