.NET Core 3.0重复执行网络请求触发SSL握手异常排查
This is a known bug in .NET Core 3.0 (SDK 3.0.100) tied to connection pooling logic in the underlying SocketsHttpHandler—the component that powers both HttpWebRequest and HttpClient in .NET Core.
What's causing the issue?
When you make the first request, the framework establishes an SSL/TLS connection to the server and adds it to a connection pool for reuse. However, a flaw in the connection management code means this connection isn't properly reset or marked as valid after the first request completes. When the second request tries to reuse the pooled connection, the SSL handshake fails because the connection is in an unexpected state, triggering the "The handshake failed due to an unexpected packet format" error.
Why does it work in other environments?
- .NET Framework 4.8: Uses a completely different HTTP handling stack (WinHTTP) that doesn't have this specific connection pooling bug.
- .NET Core 3.0 Docker containers: The network environment or default configuration in containers may avoid triggering the bug (e.g., different TCP settings or server-side connection handling behavior).
- PowerShell's
Invoke-WebRequest: Relies on the same WinHTTP stack as .NET Framework on Windows, so it's unaffected by the .NET Core-specific issue.
Fixes and workarounds
Upgrade to a newer .NET Core version (recommended)
This bug was fully resolved in .NET Core 3.1 and all subsequent versions. Upgrading to .NET Core 3.1 or later will fix the issue without requiring any code changes.Disable connection reuse temporarily
If you can't upgrade immediately, you can bypass the connection pool to force a fresh connection for each request:- For
HttpWebRequest:private static async Task CallGoogle() { var request = (HttpWebRequest)WebRequest.Create("https://google.com"); request.KeepAlive = false; // Disable connection reuse var response = (HttpWebResponse)await request.GetResponseAsync(); var jsonResponse = new StreamReader(response.GetResponseStream()).ReadToEnd(); Console.WriteLine(jsonResponse); } - For
HttpClient:var handler = new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.Zero // Disable connection pooling }; var client = new HttpClient(handler); await client.GetStringAsync("https://google.com"); await client.GetStringAsync("https://google.com");
Note: Disabling connection pooling can impact performance, so this should only be a temporary workaround until you can upgrade the framework.
- For
内容的提问来源于stack exchange,提问作者Elon Mallin

