C# Wcf connections causing TCP/IP Port Exhaustion - c#

I'm working on an ASP.NET MVC project and found several connections to a WCF service that are not closed and I think this is causing a TCP/IP Port Exhaustion.
I checked the server at the Resource Monitor/Network/TCP Connections and there are thousands of gray connections as "IPV6 Loopback" and at some point there are so many connections that the server stops responding on the service port.
Existis a dependency injection to work with the connections on the controllers and there is a "CloseChannel" method, but it was not called, I made some changes to it and started calling it in the Dipose method on the controllers to close the connections, but I did not get any results. The loopbacks continue to appear.
Two solutions I think to do is:
Remove the dependency injection and create the connection normaly
on each time with using.
Beside closing connections make some changes on the server as
described in this post
Doubt:
Is there any better option than the ones I proposed? If not, which one is the best in your opinion?
Thank you all!
PS.: Code used today to open and close connections:
This is called onthe controller:
IClient wcfClient = WcfChannel.CreateChannel<IClient>(connectionstr, WcfChannel.WcfBinding.NetTcpBinding);
This is the WcfChannel:
public static class WcfChannel
{
public static T CreateChannel<T>(string endpointAddress, WcfBinding wcfBinding)
{
Binding binding = null;
#region ReaderQuotas
XmlDictionaryReaderQuotas readerQuotas = new XmlDictionaryReaderQuotas()
{
MaxDepth = int.MaxValue,
MaxStringContentLength = int.MaxValue,
MaxArrayLength = int.MaxValue,
MaxBytesPerRead = int.MaxValue,
MaxNameTableCharCount = int.MaxValue
};
#endregion
switch (wcfBinding)
{
case WcfBinding.BasicHttpBinding:
case WcfBinding.NetMsmqBinding:
case WcfBinding.NetNamedPipeBinding:
throw new NotImplementedException();
case WcfBinding.NetTcpBinding:
binding = new NetTcpBinding()
{
Name = "NetTcpBinding",
MaxBufferPoolSize = long.MaxValue,
MaxBufferSize = int.MaxValue,
MaxReceivedMessageSize = int.MaxValue,
ReaderQuotas = readerQuotas,
Security = new NetTcpSecurity() { Mode = SecurityMode.None },
CloseTimeout = new TimeSpan(0, 5, 0),
OpenTimeout = new TimeSpan(0, 5, 0),
ReceiveTimeout = new TimeSpan(0, 5, 0),
SendTimeout = new TimeSpan(0, 5, 0)
};
break;
case WcfBinding.NetTcpBindingStreamed:
binding = new NetTcpBinding()
{
Name = "NetTcpBindingStreamed",
TransferMode = TransferMode.Streamed,
MaxBufferPoolSize = long.MaxValue,
MaxBufferSize = int.MaxValue,
MaxReceivedMessageSize = int.MaxValue,
ReaderQuotas = readerQuotas,
Security = new NetTcpSecurity() { Mode = SecurityMode.None },
CloseTimeout = new TimeSpan(0, 5, 0),
OpenTimeout = new TimeSpan(0, 5, 0),
ReceiveTimeout = new TimeSpan(0, 5, 0),
SendTimeout = new TimeSpan(0, 5, 0)
};
break;
case WcfBinding.WS2007HttpBinding:
case WcfBinding.WSHttpBinding:
throw new NotImplementedException();
default:
throw new NotImplementedException();
}
EndpointAddress endpoint = new EndpointAddress(endpointAddress);
var channelFactory = new ChannelFactory<T>(binding, endpoint);
T channelObj = channelFactory.CreateChannel();
return channelObj;
}
public static void CloseChannel(this object obj)
{
if (obj != null)
{
try
{
(obj as IClientChannel).Close();
}
catch (CommunicationException)
{
if (obj.GetType().GetMethod("Abort") != null)
{
(obj as IClientChannel).Abort();
}
}
catch (TimeoutException)
{
if (obj.GetType().GetMethod("Abort") != null)
{
(obj as IClientChannel).Abort();
}
}
catch (Exception)
{
//many connections doesn't have and Abort or close
}
if (obj.GetType().GetMethod("Dispose") != null)
{
(obj as IDisposable).Dispose();
}
obj = null;
}
}
public enum WcfBinding
{
BasicHttpBinding,
NetMsmqBinding,
NetNamedPipeBinding,
NetTcpBinding,
NetTcpBindingStreamed,
WS2007HttpBinding,
WSHttpBinding
}
}

I suspect that your problem is generated by the fact that the WCF session is not managed. The Net TCP Binding is a session based binding and the session needs to be managed. In contrast to ASP.NET, where the session is initiated and managed by the server, in WCF the session is initiated and managed by the client.
You can manage the session by using the ServiceContract/SessionMode, OperationContract/IsInitiating/IsTerminating annotation attributes. (documentation here: https://learn.microsoft.com/en-us/dotnet/framework/wcf/using-sessions)
On the client side, you need to call the CloseChannel method after ending the session. Also, you need to verify the channel state on all exception and call the abort method (some consideration regarding the use of Net TCP Binding client side here: https://blogs.msdn.microsoft.com/rodneyviana/2016/02/29/considerations-for-nettcpbindingnetnamedpipebinding-you-may-not-be-aware/). Also, on server side, as a best practice, one might want to enable service throttling in order to limit the sessions/service instances (https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-servicethrottlingbehavior-to-control-wcf-service-performance).

Related

Sending request to PKI Web service in .NET 6

I am trying to establish connection to external PKI SOAP web service, but not sure how to set BasicHttpBinding security in .NET 6. Constantly getting exception:
*System.ServiceModel.ProtocolException: 'The header 'Security' from the namespace 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' was not understood by the recipient of this message, causing the message to not be processed. This error typically indicates that the sender of this message has enabled a communication protocol that the receiver cannot process. Please ensure that the configuration of the client's binding is consistent with the service's binding. '
*
I am using auto generated class from wsdl, but create my own binding.
BasicHttpBinding:
public BasicHttpBinding GetCustomBinding()
{
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport)
{
Security =
{
Message =
{
ClientCredentialType = BasicHttpMessageCredentialType.Certificate
},
Transport =
{
ClientCredentialType = HttpClientCredentialType.Certificate
},
Mode = BasicHttpSecurityMode.Transport
},
MaxReceivedMessageSize = MaxMessageSizeBytes
};
return binding;
}
Creating proxy client:
public autoGeneratedClient GetClient(string endpointUrl, string dnsIdentity, string clientCertificatePath, string clientCertificatePassword, string serviceCertificatePath, int timeout = 10)
{
DnsEndpointIdentity endpointIdentity = new DnsEndpointIdentity(dnsIdentity);
EndpointAddress endpointAddress = new EndpointAddress(new Uri(endpointUrl), endpointIdentity);
//CustomBinding for eBox web service with security setup
MyCustomBinding myCustomBinding = new MyCustomBinding();
Binding binding = myCustomBinding.GetCustomBinding();
binding.CloseTimeout = new TimeSpan(0, timeout, 0);
binding.ReceiveTimeout = new TimeSpan(0, timeout, 0);
binding.SendTimeout = new TimeSpan(0, timeout, 0);
binding.OpenTimeout = new TimeSpan(0, timeout, 0);
autoGeneratedClient client = new autoGeneratedClient(binding, endpointAddress);
client.ClientCredentials.ClientCertificate.Certificate = X509CertificateFactory.GetClientCertificate(clientCertificatePath, clientCertificatePassword);
client.ClientCredentials.ServiceCertificate.DefaultCertificate = X509CertificateFactory.GetServiceCertificate(serviceCertificatePath);
client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
return client;
}

WCF UDP discovery only works for BasicHttpBinding not for NetTcpBinding

I have a WCF service that has announced two service endpoints. One is for net.tcp and one is for http.
net.tcp://localhost:11110/MyService
http://localhost:11111/MyService
// Make service discoverable
var discoveryBehavior = new ServiceDiscoveryBehavior();
var announceEndpointAddress = ConfigurationManager.AppSettings["AnnouncementEndpoint"];
var netTcpBinding = new NetTcpBinding(SecurityMode.None)
{
ReceiveTimeout = TimeSpan.MaxValue,
SendTimeout = TimeSpan.MaxValue,
OpenTimeout =TimeSpan.MaxValue,
CloseTimeout = TimeSpan.MaxValue
};
discoveryBehavior.AnnouncementEndpoints.Add(
new AnnouncementEndpoint(netTcpBinding,
new EndpointAddress(announceEndpointAddress)
));
_serviceHost.Description.Behaviors.Add(discoveryBehavior);
_serviceHost.AddServiceEndpoint(new UdpDiscoveryEndpoint());
On the client side, I am using WCF Discovery.
class Program
{
static void Main(string[] args)
{
var optionalBindings = new Binding[]
{
new NetTcpBinding(),
new BasicHttpBinding(),
new NetNamedPipeBinding()
};
var executed = false;
foreach (var binding in optionalBindings)
{
var contract = ContractDescription.GetContract(typeof(IMyService));
var endpoint = new DynamicEndpoint(contract, binding);
var factory = new ChannelFactory<IMyService>(endpoint);
var proxy = factory.CreateChannel();
try
{
var result = proxy.GetData(123456);
((ICommunicationObject) proxy).Close();
executed = true;
break;
}
catch (EndpointNotFoundException)
{
// Ignored
}
}
}
}
What confuses me is that it only works with BasicHttpBinding and never works with NetTcpBinding. So if BasicHttpBinding from optionalBindings list, i am not able to call the service although net.tcp endpoint is there.
Can anyone explain the reason for it?

C# WCF Channel Timeout on subsequent calls

I have a C# app calling a Soap client using WCF. The first call to the service succeeds, but any calls thereafter fail.
The code to setup the client is:
public WebserviceHelper(string username, string password, string webserviceURL)
{
var binding = new BasicHttpBinding();
binding.MaxBufferPoolSize = 2147483647;
binding.MaxBufferSize = 2147483647;
binding.MaxReceivedMessageSize = 2147483647;
binding.OpenTimeout = new TimeSpan(0, 1, 0);
binding.ReceiveTimeout = new TimeSpan(0, 1, 0);
binding.SendTimeout = new TimeSpan(0, 1, 0);
var endpoint = new EndpointAddress(webserviceURL);
var channelFactory = new ChannelFactory<EntityAPISoap>(binding, endpoint);
SoapClient = new EntityAPISoapClient(binding, endpoint);
SoapClient.Endpoint.Behaviors.Add(new CustomEndpointBehavior(username, password));
var isAlive = SoapClient.isAlive();
Console.WriteLine(SoapClient.State); //Opened
isAlive = SoapClient.isAlive(); //timeout exception
}
The first call to isAlive returns immediately, the second call times out with this exception:
The request channel timed out while waiting for a reply after
00:01:00. Increase the timeout value passed to the call to Request or
increase the SendTimeout value on the Binding. The time allotted to
this operation may have been a portion of a longer timeout.
I've increased the timeout, but then it just takes longer to respond and anyway the server responds within a second, so 1 minute is more than enough.

WCF Timeouts when target machine is not running

my .NET 4.5 application uses WCF net.tcp binding to cummunicate with server. The communication is pretty simple. Client just invoke one method and server returns true/false. The server must respond in 5 seconds. If not the client tries another server. The timing is critical for me.
WCF timeouts (Send, Recieve, Open, Close, Operation and ChannelInitializationTimeout) works fine when the PC with server is running. However when the PC is not running (or bad IP address is filled in config) it takes almost 30s until the exception is thrown. Is there any other timeout I must configure to get it working?
Here is my client code (nothing is placed in app.config file):
NetTcpBinding binding = new NetTcpBinding(SecurityMode.None)
{
SendTimeout = TimeSpan.FromMilliseconds(Configuration.Instance.LocalConfiguration.Failover.SendTimeout),
ReceiveTimeout = TimeSpan.FromMilliseconds(Configuration.Instance.LocalConfiguration.Failover.RecieveTimeout),
OpenTimeout = TimeSpan.FromMilliseconds(Configuration.Instance.LocalConfiguration.Failover.OpenTimeout),
CloseTimeout = TimeSpan.FromMilliseconds(Configuration.Instance.LocalConfiguration.Failover.CloseTimeout),
TransactionFlow = false,
TransactionProtocol = TransactionProtocol.Default,
TransferMode = TransferMode.Buffered,
Security = new NetTcpSecurity() { Mode = SecurityMode.None },
ReliableSession = new OptionalReliableSession() { Enabled = false },
ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas() { MaxArrayLength = 16384, MaxBytesPerRead = 4096, MaxDepth = 32, MaxNameTableCharCount = 16384, MaxStringContentLength = 8192 },
Name = "NoSecurity",
MaxReceivedMessageSize = 65535,
MaxConnections = 10,
MaxBufferSize = 65535,
MaxBufferPoolSize = 524288,
ListenBacklog = 10,
HostNameComparisonMode = HostNameComparisonMode.StrongWildcard
};
BindingElementCollection be = binding.CreateBindingElements();
TcpTransportBindingElement tcpBe = be.Find<TcpTransportBindingElement>();
tcpBe.ChannelInitializationTimeout = TimeSpan.FromMilliseconds(Configuration.Instance.LocalConfiguration.Failover.InitializationTimeout);
tcpBe.TransferMode = TransferMode.Buffered;
CustomBinding customBinding = new CustomBinding(be);
FailoverClient.ListenerClient serviceClient = new FailoverClient.ListenerClient(customBinding, new EndpointAddress(address));
serviceClient.InnerChannel.OperationTimeout = TimeSpan.FromMilliseconds(Configuration.Instance.LocalConfiguration.Failover.OperationTimeout);
ps: The exception thrown after 30s is 'System.ServiceModel.EndpointNotFoundException ... the attempt lasted for 00:00:04.123. TCP error 10060...' with nested exception 'System.Net.Sockets.SocketException ...the remote party did not properly respond after period of time...'
EDIT:
I found a workaround, but it does not anwser my question. I can use asynchronous call and wait for completion.
Task<bool> task = serviceClient.HeartbeatAsync();
try
{
if (task.Wait(5000))
{
Console.WriteLine("Task result: " + task.Result);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

Sending custom WCF Message to a service

My goal is to record wcf calls to one IIS hosted wcf service and replay them to a different wcf service. So far I have an IDispatchMessageInspector working following this example, it can log an incoming request and the corresponding reply to disk.
How can I read in a message from disk and then send it to the other service? Is there a way for the client to send a low level Message object to the service without going through the normal client proxy object?
I was able to get it to work by simply creating an IRequestChannel, reading the following helped explain how it works
Using the Message Class
WCF Data Transfer Architecture
WCF Messaging Fundamentals
The code to send the message:
private static void TestDispatchingMessage()
{
var reader = XmlDictionaryReader.CreateBinaryReader(new FileStream(#"path\request_6c6fc02f-45a7-4049-9bab-d6f2fff5cb2d.xml", FileMode.Open), XmlDictionaryReaderQuotas.Max);
var message = Message.CreateMessage(reader, int.MaxValue, MessageVersion.Soap11);
message.Headers.To = new System.Uri(#"url");
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
{
MessageEncoding = WSMessageEncoding.Mtom,
MaxReceivedMessageSize = int.MaxValue,
SendTimeout = new TimeSpan(1, 0, 0),
ReaderQuotas = { MaxStringContentLength = int.MaxValue, MaxArrayLength = int.MaxValue, MaxDepth = int.MaxValue }
};
var cf = new ChannelFactory<IRequestChannel>(binding, new EndpointAddress(#"url"));
foreach (OperationDescription op in cf.Endpoint.Contract.Operations)
{
op.Behaviors.Remove<DataContractSerializerOperationBehavior>();
op.Behaviors.Add(new ProtoBehaviorAttribute());
}
cf.Open();
var channel = cf.CreateChannel();
channel.Open();
var result = channel.Request(message);
Console.WriteLine(result);
channel.Close();
cf.Close();
}
This is what was in the IDispatchMessageInspector class:
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
var callId = Guid.NewGuid();
var action = request.Headers.Action.Substring(request.Headers.Action.LastIndexOf('/'));
var fileName = string.Format(#"path\{0}_{1}.data", action, callId);
try
{
var buffer = request.CreateBufferedCopy(int.MaxValue);
var writeRequest = buffer.CreateMessage();
using (var stream = new FileStream(fileName, FileMode.CreateNew))
{
using (var writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
{
writeRequest.WriteMessage(writer);
writer.Flush();
}
}
request = buffer.CreateMessage();
buffer.Close();
}
catch (Exception ex)
{
Log.ErrorException("Error writing", ex);
}
Log.Info("Call {0}", callId);
return callId;
}
Yes, sending raw messages should be easy if you work at the communication protocol level. Here's one of my old examples.

Categories

Resources