如何在 Xamarin.Forms 上调用 HTTP2

Posted

技术标签:

【中文标题】如何在 Xamarin.Forms 上调用 HTTP2【英文标题】:How to make calls to HTTP2 on Xamarin.Forms 【发布时间】:2020-05-10 05:29:32 【问题描述】:

gRpc for Xamarin.Forms with .Net Standart 2.0 适用于 http2,因此它应该是进行 HttpClient 调用或重用现有 gRpc 功能的某种方式。可能是我错过了什么。

重现问题的示例应用。您需要在某处托管 gRpc 服务。 WebClient 调用位于 AboutPage.xaml.cs aslo 测试项目中,其中包含 web 文件夹中的 asp core 3.1。 XamarinHttp2WithBackend GitHub

休闲指令Microsoft.com - HttpClient Stack and SSL/TLS Implementation Selector for android 和***.com - Use HTTP 2 with HttpClient in .Net 也没有帮助。

对于 Asp Core 3.1 控制台应用程序,您可以执行(如下)并且可以正常工作。它不适用于 2.2 及更低版本

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);

var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Get, $"http://123.123.123.123:1234/ping/")

    Version = new Version(2, 0),
;

var response = await client.SendAsync(req);

在 Xamarin 上使用相同会引发异常

 Java.IO.IOException: unexpected end of stream on com.android.okhttp.Address@ce6f1800 ---> Java.IO.EOFException: 
 not found: size=17 content=0000080700000000000000000000000001...
01-23 15:10:13.472 I/MonoDroid(28829):    --- End of inner exception stack trace ---
01-23 15:10:13.472 I/MonoDroid(28829):   at Java.Interop.JniEnvironment+InstanceMethods.CallIntMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x0006e] in <e7e2d009b69d4e5f9a00e6ee600b8a8e>:0 
01-23 15:10:13.472 I/MonoDroid(28829):   at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeVirtualInt32Method (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0002a] in <e7e2d009b69d4e5f9a00e6ee600b8a8e>:0 
01-23 15:10:13.472 I/MonoDroid(28829):   at Java.Net.HttpURLConnection.get_ResponseCode () [0x0000a] in <d706cf8faf5542949900cf6d57864528>:0 
01-23 15:10:13.472 I/MonoDroid(28829):   at Xamarin.Android.Net.AndroidClientHandler+<>c__DisplayClass46_0.<DoProcessRequest>b__2 () [0x00000] in <d706cf8faf5542949900cf6d57864528>:0 
01-23 15:10:13.472 I/MonoDroid(28829):   at System.Threading.Tasks.Task`1[TResult].InnerInvoke () [0x0000f] in /Users/builder/jenkins/workspace/archive-mono/2019-08/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Future.cs:534 
01-23 15:10:13.472 I/MonoDroid(28829):   at System.Threading.Tasks.Task.Execute () [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2019-08/android/release/external/corert/src/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:2319 

DEBUG解决方案设置

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidLinkMode>None</AndroidLinkMode>
<AotAssemblies>false</AotAssemblies>
<EnableLLVM>false</EnableLLVM>
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
<BundleAssemblies>false</BundleAssemblies>
<AndroidSupportedAbis>
</AndroidSupportedAbis>
<EmbedAssembliesIntoApk>false</EmbedAssembliesIntoApk>
<Debugger>Xamarin</Debugger>
<AndroidUseSharedRuntime>true</AndroidUseSharedRuntime>
<AndroidUseAapt2>false</AndroidUseAapt2>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<AndroidTlsProvider>btls</AndroidTlsProvider>
</PropertyGroup>

我的 asp 启动。我将它与grp服务一起使用。 发布为控制台单个可执行文件

public class Startup

    public void ConfigureServices(IServiceCollection services)
    
        services.AddGrpc((options =>  options.EnableDetailedErrors = true; ));
        services.AddMvc(options => options.EnableEndpointRouting = false);

        //services.AddDbContext<PuvDbContext>();
        services.AddScoped<IAccountService, AccountService>();
        services.AddSingleton<IFirebirdService, FirebirdService>();
        services.AddSingleton<IClassificatorService, ClassificatorService>();
        services.AddSingleton<IClassificatorRepository, ClassificatorRepository>();

        services.AddControllers();
    

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    
        if (env.IsDevelopment())
        
            app.UseDeveloperExceptionPage();
        

        app.UseRouting();
        app.UseMvcWithDefaultRoute();

        app.UseStaticFiles();
        app.UseMvc();

        app.UseEndpoints(endpoints =>
        
            endpoints.MapGrpcService<GreeterService>();
            endpoints.MapGrpcService<AccountController>();
            endpoints.MapGrpcService<ReviewController>();
            endpoints.MapGrpcService<StaticDataController>();
            endpoints.MapGrpcService<TaskController>();
            endpoints.MapControllers();
        );


    

我调用的控制器方法

[Route("files")]
public class FileController : Controller

    public FileController()
           
    

    [HttpGet("hi")]
    public async Task<HttpResponseMessage> GetTest()
    
        return new HttpResponseMessage(HttpStatusCode.OK);
    

【问题讨论】:

当您进入 Android 项目选项并确保“HttpClient 实现”显示为 AndroidClientHandler 以及“SSL/TLS 实现”是什么? Xamarin.Android.Net.AndroidClientHandlerbtls 当您将配置更改为发布时,您是否仍然看到问题?它也适用于 ios 吗?您可以分享将客户端处理程序设置为 okhttp 的代码吗? 我在 Xamarin.Forms 中调用 grpc 服务没有问题。就像我说的 grpc 对我有用,但调用 http2 不起作用。 @valentasm 由于 xamarin 为您工作,您能否看看我发布的以下问题? ***.com/questions/60360430/… 【参考方案1】:

有几种可能的解决方案:

    如果您只是将 Target 框架更新到 .NET Standard 2.1 或更高版本,那么应该可以解决您的问题。如果您不能这样做,因为您的解决方案较旧,请尝试其余的。 打开您的 Android 项目选项,然后在“Android build”中,将 HttpClient 实现值更新为“AndroidClientHandler”或任何其他值,然后重试,这已添加到该配置的 CSPROJ 中
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<AndroidTlsProvider>btls</AndroidTlsProvider>

然后我只需要在我的 HttpClient 中传递这个 HttpClientHandler:

httpClient = new HttpClient(new HttpClientHandler()

    UseProxy = true
);
    如果这也不起作用,那么您必须使用 http2dotnet (https://github.com/Matthias247/http2dotnet) 或 httptwo (https://github.com/Redth/HttpTwo) 这两个 nuget 包之一,如他们的 GitHub 中所示:
// Uri to request
var uri = new Uri ("http://somesite.com:80/index.html");

// Create a Http2Client
var http2 = new Http2Client (uri);

// Specify any custom headers
var headers = new NameValueCollection ();
headers.Add ("some-header", "value");

// For some requests you may have a request body
byte[] data = null; 

// Await our response
var response = await http2.Send (uri, HttpMethod.Get, headers, data); 

【讨论】:

1. .NET Core 3.0 和 Xamarin。我想你在这里误解了 2。我说我试过了。不要帮忙。 3. 我想我已经尝试过这两种方法了。当然,我现在必须尝试新鲜的头部。 1,2 建议没有帮助。它也可能与服务器有关。我想我试过 http2dotnet 没有成功。由于 grpc-web 已经发布,几乎可以肯定它会起作用。

以上是关于如何在 Xamarin.Forms 上调用 HTTP2的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Xamarin.Forms 中使用 Tap Gesture 调用另一个页面?

Xamarin Forms - 在点击手势识别器上添加调用对象作为参数

如何从 Xamarin Forms 项目调用 Android 和 iOS 项目中可用的方法?

Xamarin.forms - 如何在 MapView 上创建可拖动元素(标记)?

Xamarin.Forms绑定初始值后不更新

Xamarin.Forms 调用 腾讯地图SDK