【发布时间】:2020-03-15 04:05:58
【问题描述】:
问题是下面的代码如果你运行一次就会成功,但如果你在程序的同一生命周期内运行两次就会失败。
var request = (HttpWebRequest)WebRequest.Create("https://google.com");
var response = (HttpWebResponse)await request.GetResponseAsync();
我有 .NET Core 3.0 和 SDK 版本 3.0.100。
转载:
-
dotnet new console -n test-sslcd test-ssl. - 打开
Program.cs。 - 将内容替换为:
using System;
using System.IO;
using System.Net;
using System.Threading.Tasks;
namespace test_ssl
{
class Program
{
static async Task Main(string[] args)
{
await CallGoogle();
await CallGoogle();
}
private static async Task CallGoogle()
{
var request = (HttpWebRequest)WebRequest.Create("https://google.com");
var response = (HttpWebResponse)await request.GetResponseAsync();
var jsonResponse = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.WriteLine(jsonResponse);
}
}
}
-
dotnet run。
这将崩溃,但异常:
Unhandled exception. System.Net.WebException: The SSL connection could not be established, see inner exception. The handshake failed due to an unexpected packet format.
---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.IO.IOException: The handshake failed due to an unexpected packet format.
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Security.SslStream.ThrowIfExceptional()
at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result)
at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
at System.Net.Security.SslStream.<>c.<AuthenticateAsClientAsync>b__65_1(IAsyncResult iar)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncUnbuffered(Task`1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts)
at System.Net.HttpWebRequest.SendRequest()
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
--- End of inner exception stack trace ---
at System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
at System.Net.WebRequest.<>c.<GetResponseAsync>b__68_2(IAsyncResult iar)
at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)
--- End of stack trace from previous location where exception was thrown ---
at test_ssl.Program.CallGoogle() in C:\Users\me\source\Workspaces\tests\test-ssl\Program.cs:line 19
at test_ssl.Program.Main(String[] args) in C:\Users\me\source\Workspaces\tests\test-ssl\Program.cs:line 13
at test_ssl.Program.<Main>(String[] args)
为什么第二次会失败?
这个问题也发布在 github 上的 .NET Core 中,因为它似乎与此相关:https://github.com/dotnet/core/issues/3847
进一步测试以缩小问题范围
.NET Framework 4.8 - 工作中
使用 .NET Framework 4.8 控制台应用程序 确实有效。所以使用原始的Program.cs 文件在这里有效。似乎真正的问题在于 .NET Core 3.0
.NET Core 3.0 在 Docker - 工作中
docker run --rm -it mcr.microsoft.com/dotnet/core/sdk:3.0
dotnet new console -n test-ssl
cd test-ssl
// Replace content of Program.cs again.
dotnet run
// Everything works.
这让我相信我的机器上有些东西坏了,也可能不是防火墙问题,因为最终 docker 容器将通过与我的计算机相同的防火墙到达谷歌。
Powershell 测试 - 工作
test-ssl.ps1
Invoke-WebRequest -Uri 'https://google.com'
Invoke-WebRequest -Uri 'https://google.com'
HttpClient .NET Core 3.0 - 不工作
使用HttpClient 会出现同样的错误。所以用下面的替换CallGoogle的内容是行不通的:
var client = new HttpClient();
await client.GetStringAsync("https://google.com");
【问题讨论】:
-
错误显示
The handshake failed due to an unexpected packet format.。您不能重用 WebRequest 对象,它们代表 single 请求。你为什么不使用HttpClient?没有理由继续使用 HttpWebRequest -
特别是如果您使用 .NET Core 3.0,则没有理由仍然使用 HttpWebRequest。创建客户端一次,例如
var client=new HttpClient("http://www.google.com");并从多个线程调用GetStringAsync()任意次数 -
在
CallGoogle中将WebRequest替换为var client = new HttpClient(); await client.GetStringAsync("https://google.com");仍然给我同样的错误。但是,如果我有一个 .NET Framework 4.8 控制台应用程序,则原始代码可以工作。 -
无复制。 HttpClient 在 .NET Core 3.0 和所有以前的版本中运行良好。如果没有,任何使用它调用 HTTP API 的人都会注意到这个问题。
-
您是否位于可能正在运行的防火墙后面?你使用像 Fiddler 这样的调试代理吗?
docker run有什么意义?您是否在 inside 容器中运行此代码?
标签: c# ssl .net-core tls1.2 .net-core-3.0