仅在发送大请求时使用 .NET 5 时出现 SocketException (10054)

Posted

技术标签:

【中文标题】仅在发送大请求时使用 .NET 5 时出现 SocketException (10054)【英文标题】:SocketException (10054) while using .NET 5 only when large request is sent 【发布时间】:2021-12-08 06:52:00 【问题描述】:

我正在将我的 WCF 客户端从 .NET Framework 4.6.1 迁移到 .NET 5,并且每次发送大请求(如 500KB)时都会遇到 SocketException。您可以在下面找到异常、服务器配置、.NET Framework 配置和 .NET 5 配置。

例外:

System.ServiceModel.CommunicationException: Error while copying content to a stream.
 ---> System.Net.Http.HttpRequestException: Error while copying content to a stream.
 ---> System.IO.IOException: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host..
 ---> System.Net.Sockets.SocketException (10054): An existing connection was forcibly closed by the remote host.
   --- End of inner exception stack trace ---
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
   at System.Net.Security.SslStream.<WriteSingleChunk>g__CompleteWriteAsync|177_1[TIOAdapter](ValueTask writeTask, Byte[] bufferToReturn)
   at System.Net.Security.SslStream.WriteAsyncChunked[TIOAdapter](TIOAdapter writeAdapter, ReadOnlyMemory`1 buffer)
   at System.Net.Security.SslStream.WriteAsyncInternal[TIOAdapter](TIOAdapter writeAdapter, ReadOnlyMemory`1 buffer)
   at System.Net.Http.HttpConnection.WriteAsync(ReadOnlyMemory`1 source, Boolean async)
   at System.ServiceModel.Channels.BufferedMessageContent.SerializeToStreamAsync(Stream stream, TransportContext context)
   at System.Net.Http.HttpContent.<CopyToAsync>g__WaitAsync|56_0(ValueTask copyTask)
   --- End of inner exception stack trace ---
   at System.Net.Http.HttpContent.<CopyToAsync>g__WaitAsync|56_0(ValueTask copyTask)
   at System.Net.Http.HttpConnection.SendRequestContentAsync(HttpRequestMessage request, HttpContentWriteStream stream, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnection.SendRequestContentWithExpect100ContinueAsync(HttpRequestMessage request, Task`1 allowExpect100ToContinueTask, HttpContentWriteStream stream, Timer expect100Timer, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.AuthenticationHelper.SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, Boolean async, ICredentials credentials, Boolean isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
   at System.Net.Http.AuthenticationHelper.SendWithAuthAsync(HttpRequestMessage request, Uri authUri, Boolean async, ICredentials credentials, Boolean preAuthenticate, Boolean isProxyAuth, Boolean doRequestAuth, HttpConnectionPool pool, CancellationToken cancellationToken)
   at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpClient.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpClientRequestChannel.HttpClientChannelAsyncRequest.SendRequestAsync(Message message, TimeoutHelper timeoutHelper)
   --- End of inner exception stack trace ---
   at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result)
   at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass1_0.<CreateGenericTask>b__0(IAsyncResult asyncResult)
--- End of stack trace from previous location ---
   at EQ.SP.SOS.Adapter.Application.Services.AlertsService.GetUnreadAlertsAsync() in C:\Repozytoria\ShareholderPlatform\APP\EQ.SP.SOS.Adapter\EQ.SP.SOS.Adapter.Application\Services\AlertsService.cs:line 53
   at EQ.SP.SOS.Adapter.API.Controllers.AlertsController.GetUnreadAlerts() in C:\Repozytoria\ShareholderPlatform\APP\EQ.SP.SOS.Adapter\EQ.SP.SOS.Adapter.API\Controllers\AlertsController.cs:line 26
   at lambda_method537(Closure , Object )
   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
   at EQ.SP.SOS.Adapter.API.Middleware.RequestTransferAgentMiddleware.Invoke(HttpContext context, ITransferAgentContext transferAgentContext) in C:\Repozytoria\ShareholderPlatform\APP\EQ.SP.SOS.Adapter\EQ.SP.SOS.Adapter.API\Middleware\RequestTransferAgentMiddleware.cs:line 22
   at EQ.SP.SOS.Adapter.API.Middleware.RequestMetaDataMiddleware.Invoke(HttpContext context, IMetaDataContext metadataContext, IConfiguration config) in C:\Repozytoria\ShareholderPlatform\APP\EQ.SP.SOS.Adapter\EQ.SP.SOS.Adapter.API\Middleware\RequestMetaDataMiddleware.cs:line 22
   at EQ.SP.SOS.Adapter.API.Middleware.RequestUserDataMiddleware.Invoke(HttpContext context, IUserDataContext userDataContext, IConfiguration config) in C:\Repozytoria\ShareholderPlatform\APP\EQ.SP.SOS.Adapter\EQ.SP.SOS.Adapter.API\Middleware\RequestUserMiddleware.cs:line 24
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

服务器配置:

<wsHttpBinding>
    <binding name="wsbinding" closeTimeout="10:01:00" openTimeout="10:01:00" receiveTimeout="10:10:00" sendTimeout="10:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="false" allowCookies="false">
        <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
        <security mode="Transport">
            <transport clientCredentialType="Certificate" />
        </security>
    </binding>
<wsHttpBinding>
.NET Framework 4.6.1 client config:

<wsHttpBinding>
    <binding name="wsbinding" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
        <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
        <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
        <security mode="Transport">
            <transport clientCredentialType="Certificate" />
        </security>
    </binding>
</wsHttpBinding>

.NET 5 客户端配置:

private static void RegisterWcfClient<TIService, TClient>(this IServiceCollection services, string baseUrl,
    string serviceName, string certName, string storeLocation)
    where TIService : class
    where TClient : ClientBase<TIService>, new()


    services.AddScoped(servicesCollection =>
    

        var client = new TClient();
        var customBinding = new CustomBinding();

        var httpsBindingElement = new HttpsTransportBindingElement
        
            AllowCookies = true,
            MaxBufferSize = int.MaxValue,
            MaxReceivedMessageSize = int.MaxValue,
            RequireClientCertificate = true
        ;

        customBinding.Elements.Add(httpsBindingElement);

        client.Endpoint.Binding = customBinding;
        client.Endpoint.Address = new EndpointAddress($"baseUrl/serviceName");

        var storeLocationEnum = ToStoreLocationEnum(storeLocation);
        client.ClientCredentials.ClientCertificate.SetCertificate(storeLocationEnum, StoreName.My,
            X509FindType.FindBySubjectName, certName);

        return client;
    );

    services.AddScoped(servicesCollection =>
    
        var factory = servicesCollection.GetService<TClient>()?.ChannelFactory;
        return factory.CreateChannel();
    );

.NET 5 替代配置:

private static void RegisterWcfClient<TIService, TClient>(this IServiceCollection services, string baseUrl,
    string serviceName, string certName, string storeLocation)
    where TIService : class
    where TClient : ClientBase<TIService>, new()

    services.AddScoped(servicesCollection =>
    
        var client = new TClient();
        var customBinding = new WSHttpBinding(SecurityMode.Transport, false);


        customBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
        customBinding.ReaderQuotas.MaxDepth = 2147483647;
        customBinding.ReaderQuotas.MaxStringContentLength = 2147483647;
        customBinding.ReaderQuotas.MaxArrayLength = 2147483647;
        customBinding.ReaderQuotas.MaxBytesPerRead = 2147483647;
        customBinding.ReaderQuotas.MaxNameTableCharCount = 2147483647;

        customBinding.CloseTimeout = new TimeSpan(0, 10, 0);
        customBinding.OpenTimeout = new TimeSpan(0, 10, 0);
        customBinding.ReceiveTimeout = new TimeSpan(0, 10, 0);
        customBinding.SendTimeout = new TimeSpan(0, 10, 0);
        customBinding.BypassProxyOnLocal = false;
        customBinding.TransactionFlow = false;
        customBinding.MaxBufferPoolSize = 2147483647;
        customBinding.MaxReceivedMessageSize = 2147483647;
        customBinding.TextEncoding = Encoding.UTF8;
        customBinding.UseDefaultWebProxy = true;
        customBinding.AllowCookies = false;


        customBinding.ReliableSession.Ordered = true;
        customBinding.ReliableSession.InactivityTimeout = new TimeSpan(0, 10, 0);

        client.Endpoint.Binding = customBinding;
        client.Endpoint.Address = new EndpointAddress($"baseUrl/serviceName");


        var storeLocationEnum = ToStoreLocationEnum(storeLocation);
        client.ClientCredentials.ClientCertificate.SetCertificate(storeLocationEnum, StoreName.My,
            X509FindType.FindBySubjectName, certName);

        return client;
    );

    services.AddScoped(servicesCollection =>
    
        var factory = servicesCollection.GetService<TClient>()?.ChannelFactory;
        return factory.CreateChannel();
    );

它适用于小请求,但对于较大的请求,我会提到异常。 我根据我在 .NET 5 中的配置从头开始创建了 .NET 4.6.1 WCF 客户端,并且在 .NET 4.6.1 上一切正常。 关于异常 - 我第一次发送请求时,错误出现需要 20 秒。对于下一个请求,它的速度要快得多(比如 1-2 秒)。

到目前为止我的调查:

在官方 WCF repo https://github.com/dotnet/wcf/issues/4751 上创建问题 在服务器上禁用证书吊销检查但没有成功 检查了事件查看器和 PerfView 是否有任何相关异常,但没有发现任何异常 尝试修改 HTTP 请求上的 Connection 标头,但没有成功

我不知道这个错误的根本原因是什么。这似乎是一个服务器端问题,但为什么它在 .NET 4.6.1 客户端上运行良好?

知道那里可能出了什么问题吗?

【问题讨论】:

【参考方案1】:

您的错误是:System.ServiceModel.CommunicationException:将内容复制到流时发生错误。 可能的原因是主机已经到了空闲超时时间,启动时间过长。由于代码或设计问题,主机可能会意外重启。

【讨论】:

这更适合作为对问题的评论。

以上是关于仅在发送大请求时使用 .NET 5 时出现 SocketException (10054)的主要内容,如果未能解决你的问题,请参考以下文章

从 Angular 向 Asp.net 发送 post 请求时出现错误 405

向 ASP.NET Web API 发送请求时出现 CORS 错误

访问器仅在针对 ECMAScript 5 及更高版本时可用 - 使用 TypeScript 时出现错误消息

使用sudo命令时出现如下错误,为啥应当如何处理

发送以下 http 获取请求时出现错误。我使用颤振 2.5

使用 Ktor 发送发布请求时出现内部服务器错误