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.
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'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;
}
I'm implementing a Silverlight application that uses WCF services heavily, I've got to the point now where occasionally there are several long service calls that block other service calls from running.
These service calls eventually time out. I'd like to see if its possible to a queue system that executes service calls one after another, this way long calls will hold up other calls but won't cause them to timeout.
I'm using service agents to wrap the service calls
public interface IExampleServiceAgent
{
void ProcessData(int a, string b, EventHandler<ProcessDataCompletedEventArgs> callback);
}
Public ExampleServiceAgent1 : IExampleServiceAgent
{
ExampleClient _Client = new ExampleClient();
public void ProcessData(int anInt, string aString, EventHandler<ProcessDataCompletedEventArgs> callback)
{
EventHandler<ProcessDataCompletedEventArgs> wrapper = null;
wrapper = (a,b) =>
{
callback(a,b);
_Client.ProcessDataCompleted -= wrapper;
}
_Client.ProcessDataCompleted += wrapper;
_Client.ProcessDataAsync(anInt,aString);
}
}
The above service agent would then be called from code as follows:
ServiceAgent.ProcessData(1,"STRING", (a,b) =>
{
if (b.Error != null)
{
//Handle Error
}
else
{
//DO something with the data
}
}
Is there a way I could put these service calls into a queue and execute them one by one?
I've tried wrapping them as Actions and adding them to a queue, but this does not wait for one to finish executing before starting the next one and although they do call the service correctly no data is returned to the calling ViewModel.
WCF services can cope with a huge number of calls but, to avoid denial of service attacks, the number of requests that can be processed is limited by default.
The significant limitations for Silverlight WCF services would be
A default limit of 2 simultaneous calls from the same IP address.
A limit of approx 10-16 concurrent connections (documentation varies on this one).
This CodeProject article on Quick Ways to Boost Performance and Scalability of ASP.NET, WCF and Desktop Clients was useful.
I am guessing you are immediately hitting the first issue. In your WCF config you need to add the following to increase the single IP connections:
<system.net>
<connectionManagement>
<add address="*" maxconnection="100" />
</connectionManagement>
</system.net>
You may then hit the second limit for which the solution is tweak the service behaviors in the web/app.config files.
Here are a few more references I found while sorting out these issues myself:
http://weblogs.asp.net/paolopia/archive/2008/03/23/wcf-configuration-default-limits-concurrency-and-scalability.aspx
Why does WCF limit concurrent connections to 5?
http://msdn.microsoft.com/en-us/magazine/cc163590.aspx#S10
http://blogs.msdn.com/b/stcheng/archive/2009/01/06/wcf-things-that-will-impact-concurrency-capacity-behavior-of-wcf-service-with-simoultaneous-client-requests-connections.aspx
http://www.codeproject.com/Articles/133738/Quick-Ways-to-Boost-Performance-and-Scalability-of
http://www.danrigsby.com/blog/index.php/2008/02/20/how-to-throttle-a-wcf-service-help-prevent-dos-attacks-and-maintain-wcf-scalability/
http://msdn.microsoft.com/en-us/library/7w2sway1%28v=vs.71%29.aspx
http://www.codeproject.com/Articles/89858/WCF-Concurrency-Single-Multiple-and-Reentrant-and
WCF stops responding after about 10 or so calls (throttling)
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();
}