WCF Client works but occasionally hits an Unknown Socket Error

Viewed 320

I have a WCF Client running in .NET Core 5.0.4 on a Linux machine (Raspberry Pi running Buster Lite). The client runs fine most of the time, but occasionally hits an "Unknown socket error". When this happens, it takes ~1-5 minutes before the client can connect again. During that time, other clients (SoapUI) can connect to the server (C++ application using gSOAP) without issue.

My client code (shown below) is based on the answer to this SO post and has a single static client for the entire application. One thing that I changed was removing the [ThreadStatic] as I was still seeing multiple client connections.

This is a fairly small application typically with only one user, so I'm not too concerned about performance with sharing a single client. It would be possible for multiple threads to attempt a client request at the same time, but from what I understand, the WCF client is thread safe and should be ok with that.

I had previously tried opening and closing the client on every request, but the "Unknown socket error" seemed to occur more frequently.

Any ideas on what might be causing this "Unknown socket error"?

The exception:

"2021-06-08 13:27:41.921 [ERR] FAIL: Failed SOAP command. Exception:Unknown socket error (localhost:1234)
System.ServiceModel.CommunicationException: Unknown socket error (localhost:1234)
 ---> System.Net.Http.HttpRequestException: Unknown socket error (localhost:1234)
 ---> System.Net.Sockets.SocketException (0xFFFDFFFE): Unknown socket error
   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.Sockets.Socket.<ConnectAsync>g__WaitForConnectWithCancellation|283_0(AwaitableSocketAsyncEventArgs saea, ValueTask connectTask, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.DefaultConnectAsync(SocketsHttpConnectionContext context, CancellationToken cancellationToken)
   at System.Net.Http.ConnectHelper.ConnectAsync(Func`3 callback, DnsEndPoint endPoint, HttpRequestMessage requestMessage, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at System.Net.Http.ConnectHelper.ConnectAsync(Func`3 callback, DnsEndPoint endPoint, HttpRequestMessage requestMessage, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
   at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, 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.ServiceModel.Channels.ServiceModelHttpMessageHandler.SendAsync(HttpRequestMessage request, 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.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(HttpRequestException requestException, HttpRequestMessage request, HttpAbortReason abortReason)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpClientRequestChannel.HttpClientChannelAsyncRequest.SendRequestAsync(Message message, TimeoutHelper timeoutHelper)
   at System.ServiceModel.Channels.RequestChannel.RequestAsync(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.RequestChannel.RequestAsyncInternal(Message message, TimeSpan timeout)
   at System.Runtime.TaskHelpers.WaitForCompletionNoSpin[TResult](Task`1 task)
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(MethodCall methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(MethodInfo targetMethod, Object[] args)
--- End of stack trace from previous location ---
   at System.Reflection.DispatchProxyGenerator.Invoke(Object[] args)
   at generatedProxy_1.MySOAPRequestTest(MySOAPRequestTestRequest )
   at SOAPTest.TestPortClient.SOAPTest.TestPort.MySOAPRequestTest(MySOAPRequestTestRequest request) in /home/<my local path>/Reference.cs:line 10501
   at SOAPTest_DLL.My_StdTestSet.soapTest(Int32 value) in /home/<my local path>/SOAPTest_DLL/My_StdTestSet.cs:line 1012

My code:

using Serilog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;

namespace SOAP_DLL
{
    public class SOAPTestClient 
    {
        //[ThreadStatic]
        private static SOAPTest.TestPortClient _client;

        private static Int32 SOAPClientCounter = 0;

        private static string _Endpoint = "http://localhost:1234/Test.cgi";
        public static string Endpoint
        {
            get { return _Endpoint; }
            private set { _Endpoint = value; }
        }

        private static string _Address = "localhost";
        public static string Address
        {
            get { return _Address; }
            private set { _Address = value; }
        }

        private static string _Port = "1234";
        public static string Port
        {
            get { return _Port; }
            private set { _Port = value; }
        }

        public SOAPTestClient(string address, string port)
        {
            if (SpecifySpecificDevices.type == null)
            {
                SpecifySpecificDevices.type = new SOAPTest.DeviceTypes();
            }
            if (SpecifySpecificDevices.group == null)
            {
                SpecifySpecificDevices.group = new SOAPTest.DeviceGroups();
            }

            if (address != Address || port != Port)
            {
                //User changed address or port, reinit
                Address = address;
                Port = port;
                Log.Information("SOAP Address/Port mismatch. Initializing client.");
                InitializeClient();
            }
        }

        //Client reuses the static _client for all requests.  If anything fails, it will reconnect.
        //If the config changes, it will reconnect too.
        public SOAPTest.TestPortClient Client
        {
            get
            {
                if (_client == null || 
                    (_client.State != CommunicationState.Opened 
                    && _client.State != CommunicationState.Opening 
                    && _client.State != CommunicationState.Created))
                {
                    string inf = "SOAP Client must be initialized.";
                    if (_client != null)
                    {
                        inf += " State: " + _client.State.ToString();
                    }
                    Log.Information(inf);

                    InitializeClient();
                }
                return _client;
            }
        }

        private void InitializeClient()
        {
            if (_client != null)
            {
                if (_client.State == CommunicationState.Faulted)
                {
                    _client.Abort();
                }
                else
                {
                    try
                    {
                        _client.CloseAsync();
                        --SOAPClientCounter;
                        Log.Information("SOAP client close. Present state: " + _client.State.ToString() + ". Num clients open now: "
                            + SOAPClientCounter.ToString());
                    }
                    catch (CommunicationException e)
                    {
                        Log.Error(e, "SOAP Client failed to close - comm exception");
                        _client.Abort();
                    }
                    catch (TimeoutException e)
                    {
                        Log.Error(e, "SOAP Client failed to close - timeout exception");
                        _client.Abort();
                    }
                    catch (Exception e)
                    {
                        Log.Error(e, "SOAP Client failed to close - exception");
                        _client.Abort();
                    }
                }
            }

            string tmpEndpoint = "http://" + Address + ":" + _Port + "/Test.cgi";
            Log.Information("Opening new SOAP client connection to " + tmpEndpoint);
            try
            {
                _client = new SOAPTest.TestPortClient(SOAPTest.TestPortClient.EndpointConfiguration.Test, tmpEndpoint);
                ++SOAPClientCounter;
                Log.Information("SOAP Client opened successfully. Total open clients = " + SOAPClientCounter.ToString());
            }
            catch(Exception e)
            {
                Log.Error(e, "Failed to connect to SOAP server at " + tmpEndpoint);
            }
        }
    }
}
0 Answers
Related