I'm using a one-way WCF service contract to push data from a server to a set of registered clients. The application mostly works fine, however users report occasional disconnects in the production environment.
When I try to reproduce the error in development, eg. by killing the server, I see the cause of the exception is available in the $exception debugger variable (as shown below). I've read here that it's not possible to retrieve the value of this error in code, but is it possible to catch and log this error using some technique or configuration?
I imagine it would be quite straightforward in a two way RPC type call, but with the one way contract it's not as clear from the documentation. I would love to be able to be able to run the WCF network trace tool, but because the disconnects are so intermittent, and the production environment rather serious it's not something I can do very easily.
Here is a snippet of the service contract:
[OperationContract(IsOneWay = true)]
void SendGridState(GridState grid);
The service binding:
var binding = new NetTcpBinding
{
MaxBufferPoolSize = int.MaxValue,
MaxBufferSize = int.MaxValue,
MaxReceivedMessageSize = int.MaxValue,
ReceiveTimeout = TimeSpan.MaxValue,
ReliableSession = { InactivityTimeout = TimeSpan.MaxValue }
};
The faulted event handler:
clientChannel.Faulted += delegate
{
connectionStatus = CommunicationState.Faulted;
}
Related
I have created a simple client-service setup for downloading applications from a server. The client creates a NetNamedPipeBinding and uses this to open a channel with the service to initiate the download process.
private void OpenServiceChannel()
{
InstanceContext instanceContext = new InstanceContext(this);
NetNamedPipeBinding binding = new NetNamedPipeBinding();
DuplexChannelFactory<ICloudProductService> duplexChannelFactory = new DuplexChannelFactory<ICloudProductService>(instanceContext, binding, new EndpointAddress(ADDRESS));
_proxy = duplexChannelFactory.CreateChannel();
}
The service then uses a Callback on the same channel to report progress to the client. This callback is defined as a part of the ServiceContract:
[ServiceContract(CallbackContract = typeof(IStatusProgressCallback))]
public interface ICloudProductService
{
[OperationContract]
[FaultContract(typeof(ServiceErrorCode))]
Task<IEnumerable<LauncherAppDetails>> GetAvailableApps(string tokenJson);
[OperationContract]
[FaultContract(typeof(ServiceErrorCode))]
Task Install(string tokenJson, LauncherAppDetails app);
}
With the event in said callback triggered by the Service when it needs to increment the download progress
public interface IStatusProgressCallback
{
[OperationContract]
void OnProgressUpdated(InstallProgress progress);
}
This all works beautifully on any half decent internet connection as the downloads are pretty fast. My issue is that on slow connections, the download is taking longer than the 1 minute default timeout for the binding. I could increase the timeout of this binding but I am concerned that this is not an ideal solution as it is bad practice due to possible security/performance issues.
Is there a way of configuring the binding on the client side so that the timeout period is refreshed on each successful callback (a keep-alive) such that as long as the server is passing incremental download progress, the connection will not time out.
The following code works just fine in WinForms:
if (System.Configuration.ConfigurationManager.AppSettings["Data.UseNamedPipes"] == "true")
{
AES.Cloud.DataAccessLayer.TheEDGEContext.UseLocalDb = true;
string address = "net.pipe://localhost/" + Guid.NewGuid() + "/DataManager";
var svc = AES.InProcFactory.CreateInstance<AES.Cloud.DataService.EstimatingDataService, AES.Cloud.DataService.IDataManager>(2147483647, 2147483647, address);
DataService<AES.TheEdge.Framework.Services.Contracts.IDataManager>._namedPipeAddress = address;
var ss = svc.GetNextKey(); //<-- Calling service here.
}
but when I use WPF it times out (exception at the bottom but it's useless) when I call the service, however if I invoke this code on a different thread it works just fine in WPF:
System.Threading.ThreadPool.QueueUserWorkItem( new System.Threading.WaitCallback( (o) =>{
//same code as above... no problem, no timeout, no blocking
}
Is there some issue using the startup/main thread to do this sort of thing? Confused in Jupiter...
Exception:
System.TimeoutException: This request operation sent to net.pipe://localhost/c1a59720-11fc-49c3-9d82-8185203a6f5d/DataManager did not receive a reply within the configured timeout (00:01:00). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.
I have a cache instance running on Windows Azure. I'm connecting to it from my web application and getting intermittent exceptions with the following message:
ErrorCode:SubStatus:There is a temporary failure.
Please retry later. (One or more specified cache servers are
unavailable, which could be caused by busy network or servers. For
on-premises cache clusters, also verify the following conditions.
Ensure that security permission has been granted for this client
account, and check that the AppFabric Caching Service is allowed
through the firewall on all cache hosts. Also the MaxBufferSize on the
server must be greater than or equal to the serialized object size
sent from the client.). Additional Information : The client was trying
to communicate with the server:
net.tcp://myserver.cache.windows.net:22234.
I've been able to duplicate the problem with this snippet in LinqPad
var config = new DataCacheFactoryConfiguration
{
AutoDiscoverProperty = new DataCacheAutoDiscoverProperty(true, "myserver.cache.windows.net"),
SecurityProperties = new DataCacheSecurity("key", false)
};
var factory = new DataCacheFactory(config);
var client = factory.GetDefaultCache();
//client.Put("foo", "bar");
for (int i = 0; i < 100; i++)
{
System.Threading.Tasks.Task.Factory.StartNew(o => {
var i1 = (int)o;
try
{
client.Get("foo").Dump();
} catch (Exception e)
{
e.Message.Dump();
}
}, i);
}
If I run this snippet as-is, spawning more than about 50 threads, I get the error. If I uncomment the initial Put(), I can run it with 10,000 threads. I make sure the entry is in the cache regardless before I run this. I've tried using pessimistic locking and it does not seem to have any effect. I've used the latest client DLLs from NuGet. I've tried scaling the cache up to 1GB with no other usage besides this snippet.
Since my requests in my web app are coming in on different threads, I believe this reasonably simulates what's happening in my app. And I'm definitely getting the same exception in both cases. Can anyone suggest a way to avoid this exception? Does it have to do with the initial Put() happening on the same thread as the constructor? That seems unlikely but it's the only thing I can do in this test scenario to eliminate the exception.
I have a piece of code that calls a WCF service that is hosted on a server.
The code keeps looping around and around calling this method over and over again. (It's asking for a 'status', so it's not doing any work at all).
That's fine except that after a short period of time I get an error:
This request operation sent to net.tcp://serverName:9001/service1 did not receive a reply within the configured timeout (00:00:09.9843754)
And suddenly i cannot get to the server at all EVER. I increased the timeout to 1min but still the same problem. Note that the program that hosts the service is doing nothing else, just offering it's 'status'. So it's not an issue with the WCF service app being busy.
I think it's a problem with the code calling the service because when i re-start the app it can connect to the service just fine ... until after another short time i get the timeout error again. For this reason i don't thnk it's a network error either, as when I restart the app it's ok for a period of time.
Here is the code i use to call the service. Do i need to dispose of the ChannelFactory after each call to clean it up or what am i doing worng?
NetTcpBinding binding = new NetTcpBinding(SecurityMode.Message);
binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
EndpointAddress endPoint = new EndpointAddress(new Uri(clientPath));
ChannelFactory<IClient> channel = new ChannelFactory<IClient>(binding, endPoint);
channel.Faulted += new EventHandler(channel_Faulted);
IClient client = channel.CreateChannel();
((IContextChannel)client).OperationTimeout = new TimeSpan(0, 0, 10);
ClientStatus clientStatus = client.GetStatus();
You do have to close client connections after you finish calling GetStatus. The best way to do this is to use a using block. But you can also do something like this after your call client.GetStatus()
ClientStatus clientStatus = client.GetStatus();
try
{
if (client.State != System.ServiceModel.CommunicationState.Faulted)
{
client.Close();
}
}
catch (Exception ex)
{
client.Abort();
}
I am trying to host a WCF service, using NetTcpBinding in a Windows service. (I'm going to use it as an API for various clients both Web and Windows32) Obviously, I am doing this within a test host before putting it in a Windows service.
I have the following contract:
namespace yyy.xxx.Server.API.WCF
{
[ServiceContract]
public interface ISecureSessionBroker
{
[OperationContract]
string GetSessionToken(string username, string encryptedPassword, string clientApiKey, string clientAddress);
}
}
with the following implementation:
namespace yyy.xxx.Server.API.WCF
{
public class SecureSessionBroker : ISecureSessionBroker
{
#region ~ from ISecureSessionBroker ~
public string GetSessionToken(string username, string encryptedPassword, string clientApiKey, string clientAddress)
{
return Guid.NewGuid().ToString();
}
#endregion
}
}
I am hosting the WCF service using the code below (within a class/method):
try
{
_secureSessionBrokerHost = new ServiceHost(typeof(SecureSessionBroker));
NetTcpBinding netTcpBinding = new NetTcpBinding();
_secureSessionBrokerHost.AddServiceEndpoint(typeof(ISecureSessionBroker), netTcpBinding, "net.tcp://localhost:8080/secureSessionBrokerTcp");
int newLimit = _secureSessionBrokerHost.IncrementManualFlowControlLimit(100);
// Open the ServiceHost to start listening for messages.
_secureSessionBrokerHost.Open();
}
catch (Exception ex)
{
throw;
}
The key thing here is that I do not want to have to rely on an App.config file. Everything must be configured programmatically. When I run this code, the service appears to come "up" and listen. (ie. I have no exceptions)
BUT when I use the client code below:
string secureSessionBrokerUrl = string.Format("{0}/secureSessionBrokerTcp","net.tcp://localhost/8080",url);
EndpointAddress endpointAddress=new EndpointAddress(secureSessionBrokerUrl);
System.ServiceModel.Channels.Binding binding = new NetTcpBinding();
yyy.xxx.Windows.AdminTool.API.WCF.SecureSessions.SecureSessionBrokerClient
client = new yyy.xxx.Windows.AdminTool.API.WCF.SecureSessions.SecureSessionBrokerClient(binding,endpointAddress);
string sessionToken=client.GetSessionToken("", "", ""); // exception here
MessageBox.Show(sessionToken);
... I always get an exception. At the moment, I am getting:
This request operation sent to
net.tcp://localhost:8080/secureSessionBrokerTcp
did not receive a reply within the
configured timeout (00:01:00). The
time allotted to this operation may
have been a portion of a longer
timeout. This may be because the
service is still processing the
operation or because the service was
unable to send a reply message.
Please consider increasing the
operation timeout (by casting the
channel/proxy to IContextChannel and
setting the OperationTimeout property)
and ensure that the service is able to
connect to the client.
So I guess it cannot resolve the service.
Where am I going wrong? How do I test for the existence of the service over TCP? I have used the SvcTraceViewer and I just get the same message, so no news there.
I would prefer to ask the user for a URL of the service, so they would enter "net.tcp://localhost:8080" or something, which would then be used as a BaseAddress for the various calls to the SecureSessionBroker (and other) WCF services ... without resorting to App.config.
Unfortunately, all the examples I can find all use the App.config.
Interestingly, I can host the service using the VS Host and the client connects fine. (Using:
D:\dev2008\xxx\yyy.xxx.Server>WcfSvcHost.exe /service:bin/debug/yyy.
xxx.Server.dll /config:App.config)
Ok, it came to me in a flash of inspiration.
I was using a Windows Form (alarm bells) to "host" the service. Clicking out of the form, I used a bit of code to call the service (included) on a button click. Of course, the service was not in its own thread, so the service could not respond.
I've fixed it by putting the Service container (which contains the host) within its own thread:
Thread thread = new Thread(new ThreadStart(_serviceWrapper.Start));
thread.Start();
The Start() method sets up the ServiceHost.
I incorrectly thought that while a WCF Service Host will create threads for incoming requests, it will only do this if it is in its own non-blocking thread (ie. not a UI thread).
Hope it helps someone else.