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);
}
Related
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;
}
I'm trying to activate transport security for a WCF Client-Server application.
It works fine on our test machine, but in the target environment the connection is always reset after what looks to me like a successful handshake:
Wireshark capture
I've tried deactivating the firewall, although the application worked fine without TLS, but it made no difference. I've also tried TLS1.1 and TLS1.0 but that made no difference either.
Here is the source code for the service host:
public void Start()
{
var binding = new NetTcpBinding(SecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
binding.Security.Transport.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
binding.ReliableSession.Enabled = true;
binding.ReliableSession.InactivityTimeout = TimeSpan.FromSeconds(30);
binding.ReceiveTimeout = TimeSpan.FromSeconds(5);
binding.SendTimeout = TimeSpan.FromSeconds(5);
binding.MaxReceivedMessageSize = 64 * 1048576;
binding.ReaderQuotas.MaxArrayLength = 2147483647;
binding.ReaderQuotas.MaxStringContentLength = 2147483647;
this.host = new ServiceHost(
this,
new[] { new Uri(string.Format("net.tcp://{0}:{1}", this.hostname, this.port)) }
);
this.host.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
this.host.AddServiceEndpoint(new UdpDiscoveryEndpoint());
this.host.AddServiceEndpoint(typeof(ILvsService), binding, "LvsService");
this.host.Credentials.ServiceCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.My,
X509FindType.FindBySubjectName,
this.hostname
);
this.host.Open();
}
And here is the client side:
public ServiceClient(string server, ushort port)
{
var binding = new NetTcpBinding(SecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
binding.Security.Transport.SslProtocols = System.Security.Authentication.SslProtocols.Tls12;
binding.MaxReceivedMessageSize = 64 * 1048576;
binding.ReaderQuotas.MaxArrayLength = 2147483647;
binding.ReaderQuotas.MaxStringContentLength = 2147483647;
binding.ReceiveTimeout = TimeSpan.FromSeconds(5);
binding.SendTimeout = TimeSpan.FromSeconds(5);
var context = new InstanceContext(this);
this.channelFactory = new DuplexChannelFactory<ILvsService>(
context,
binding,
new EndpointAddress(string.Format("net.tcp://{0}:{1}/LvsService", server, port))
);
}
The server is running as a service on a Windows Server 2016 VM with .NET Framework 4.7.2. The clients are running on Windows 10 machines also with .NET Framework 4.7.2.
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).
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.
My client application use a WCF web service which is hosted in my local IIS. This web service use for upload an image. Once image size become bigger it gives bad request(400).
Client is configure to dynamically get the web service URL.
Client Code
string serviceUrl=GetUrl(); /* http://localhost:85/ImageUploaderService.svc */
TimeSpan timeOut = new TimeSpan(0, 30, 0);
EndpointAddress endPoint = new EndpointAddress(serviceUrl);
BasicHttpBinding binding = new BasicHttpBinding()
{
CloseTimeout = timeOut,
MaxReceivedMessageSize = 65536,
OpenTimeout = timeOut,
ReceiveTimeout = timeOut,
SendTimeout = timeOut,
MaxBufferSize = 65536,
MaxBufferPoolSize = 524288,
UseDefaultWebProxy = true,
};
binding.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxArrayLength = 64000000,
MaxStringContentLength = 8192,
MaxDepth = 32,
MaxNameTableCharCount = 16384,
MaxBytesPerRead = 4096
};
client = new ImageUploaderServiceClient(binding, endPoint);
Web Service side
<basicHttpBinding>
<binding maxBufferSize="64000000" maxReceivedMessageSize="64000000" maxBufferPoolSize="64000000">
<readerQuotas maxDepth="64000000" maxStringContentLength="64000000" maxArrayLength="64000000" maxBytesPerRead="64000000" />
<security mode="None"/>
</binding>
</basicHttpBinding>
What is the wrong I am doing. Please guide me through the correct way.
You should increase MaxReceivedMessageSize on client side as well probably:
BasicHttpBinding binding = new BasicHttpBinding()
{
MaxReceivedMessageSize = 64000000,
MaxBufferSize = 64000000,
MaxBufferPoolSize = 64000000,
// .....
};
binding.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxArrayLength = 64000000,
MaxStringContentLength = 64000000,
MaxDepth = 64000000,
MaxBytesPerRead = 64000000
};
I had the same problem once - the server and client binding configuration should be the same.