I have a WCF service hosted by a Windows Service. It works but calling it is very slow: A simple function like void Test() takes around 500 ms from client call to server receive.
I tried several different configurations but haven't succeeded in making that faster. Both client and server are on the same machine.
Here is the code:
Shared.dll:
[ServiceContract]
public interface IContract
{
[OperationContract]
void Test();
}
Server.exe:
public class Service : IContract
{
public void Test()
{
this.Log("Test: " + DateTime.Now.TimeOfDay);
}
}
Client.exe:
var binding = ...;
var factory = new ChannelFactory<IContract>(binding, "net.tcp://localhost/Service");
var service = factory.CreateChannel();
this.Log("Test: " + DateTime.Now.TimeOfDay);
service.Test();
app.config:
<system.serviceModel>
<services>
<service behaviorConfiguration="ServiceBehavior" name="Server.Service">
<endpoint address="Service" binding="netTcpBinding" bindingConfiguration="NetTcp" contract="Shared.IContract">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost/" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="NetTcp" portSharingEnabled="true">
<security mode="None">
<message clientCredentialType="None"/>
<transport protectionLevel="None" clientCredentialType="None"/>
</security>
<reliableSession enabled="false" />
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Edit
I am currently testing this with client and server in the same machine but the idea is to have them in different machine for production.
The factory and channel creations are not the culprit here. I ruled that out putting a Thread.Sleep(20000) between the creation and the log and got the same result.
The difference between the client log and the server log is around 500 ms for the first call (actually, it's anywhere between 300 ms and 1 second) but then, it lasts less than 5 ms for any more call to Test(). I want my function to be always very fast, including the first call. How can I do that?
Assuming you are using the Datetime.Now's to measure this, I would recommend a different approach for benchmarking.
var elapsedTimes = new List<long>();
var stopwatch = new Stopwatch();
for (var i = 0; i < 1000; i++)
{
stopwatch.Reset();
stopwatch.Start();
service.Test();
stopwatch.Stop();
elapsedTimes.Add(stopwatch.ElapsedMilliseconds);
}
Log("Average time for Test(): " + elapsedTimes.Average() + "ms");
Also, the first call always seems to be slow, probably because of initializations taking place on the service side. Try making one call to service.Test() and then running this benchmark.
Please post your results, I'd be curious to found out how it goes.
Related
I have a self-hosted C# WCF .Net 4.6.1 Windows service that communicates with another self-hosted WCF service. This works fine when both services are on the same server. However, when I move the server to another computer, I get this error:
System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. There are no firewalls running on either computer and I get a response when using http://192.168.1.129:6253/eTutorWcfService (using net.tcp in app).
Client app.config:
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IeTutorMessage" />
</basicHttpBinding>
<netTcpBinding>
<binding name="NetTcpBinding_IeTutorMessage" />
</netTcpBinding>
</bindings>
<client>
<endpoint name="BasicHttpBinding_IeTutorMessage"
address="http://localhost:6253/eTutorWcfService"
binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IeTutorMessage"
contract="eTutorServiceReference.IeTutorMessage" />
<endpoint name="NetTcpBinding_IeTutorMessage"
address="net.tcp://localhost:6254/eTutorWcfService"
binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IeTutorMessage"
contract="eTutorServiceReference.IeTutorMessage" >
<identity>
<servicePrincipalName value = ""/>
</identity>
</endpoint>
</client>
Server app.config:
<services>
<service name="eTutorServer.eTutorWcfService"
behaviorConfiguration="myeTutorServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:6253/eTutorWcfService"/>
<add baseAddress="net.tcp://localhost:6254/eTutorWcfService"/>
</baseAddresses>
</host>
<endpoint
address="http://localhost:6253/eTutorWcfService"
binding="basicHttpBinding"
contract="eTutorServer.IeTutorMessage" />
<endpoint
address="net.tcp://localhost:6254/eTutorWcfService"
binding="netTcpBinding"
contract="eTutorServer.IeTutorMessage" />
<endpoint
address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange"/>
<endpoint
address="mex"
binding="mexTcpBinding"
contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="myeTutorServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
The client code:
EndpointAddress address = new EndpointAddress("net.tcp://" + eTutorServiceIp + ":6254/eTutorWcfService");
eTutorServiceReference.IeTutorMessageClient client = new eTutorServiceReference.IeTutorMessageClient("NetTcpBinding_IeTutorMessage", address);
try
{
rtn = client.eTutorMessage(itm);
client.Close();
}
When the client tries to connect, the output window of the server shows an SecurityTokenValidationException but I'm not sure what to do about that or if it means something relevant. I'm sure this has something to do with security but I don't know what to add where.
First, Nettcpbinding use transport security mode and authenticate the client with windows credential by default.
WCF throws exception that the server has rejected the client credentials, what is the default security mode for NetTCP in WCF
Then, when we change the server configuration and re-host the service, we should re-generate the client proxy class when we calling it. besides, we may need to change the endpoint address in the client configuration since Localhost is generated by default.
I can live with this but would really like to know how to do it
without security.
At last, when we change the security to None, the client does not need to provide the credentials to invoke the service. I suggest you re-host the service and re-generate the client proxy class. I have made a demo, wish it is useful to you.
Server end(console application)
class Program
{
static void Main(string[] args)
{
using (ServiceHost sh=new ServiceHost(typeof(MyService)))
{
sh.Opened += delegate
{
Console.WriteLine("Service is ready......");
};
sh.Closed += delegate
{
Console.WriteLine("Service is closed");
};
sh.Open();
Console.ReadLine();
sh.Close();
}
}
}
[ServiceContract]
public interface IService
{
[OperationContract]
string SayHello();
}
public class MyService : IService
{
public string SayHello()
{
return "Hello Stranger";
}
}
App.config
<system.serviceModel>
<services>
<service behaviorConfiguration="Service1Behavior" name="VM1.MyService">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="mybinding" contract="VM1.IService" >
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange"/>
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:13007/"/>
</baseAddresses>
</host>
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="mybinding">
<security mode="None">
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="Service1Behavior">
<serviceMetadata />
<serviceDebug includeExceptionDetailInFaults="False"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Client end.
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
try
{
Console.WriteLine(client.SayHello());
}
catch (Exception)
{
throw;
}
App.config
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_IService">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<client>
<!--we may need to change the generated endpoint address to autual server IP address.-->
<endpoint address="net.tcp://10.157.13.69:13007/" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IService" contract="ServiceReference1.IService"
name="NetTcpBinding_IService" />
</client>
</system.serviceModel>
Feel free to let me know if there is anything I can help with.
I added the following code and it works:
client.ClientCredentials.Windows.ClientCredential.UserName = runAs;
client.ClientCredentials.Windows.ClientCredential.Password = runAsPassword;
client.ClientCredentials.Windows.ClientCredential.Domain = runAsDomain;
However, I'd like to do this without security since it will be placed on multiple servers, none of which will have a public IP. I've tried to add to the bindings but on the client it's not a valid node and on the server, it stops the service from starting. I tried to add the following code to the server but it won't open the ServiceHost:
serviceHost.AddServiceEndpoint(typeof(eTutorWcfService), new NetTcpBinding(SecurityMode.None), "");
I can live with this but would really like to know how to do it without security.
I have faced a very strange error in my WCF service, which appears to somehow create a deadlock or thread starvation in socket level when I use NetTcpBinding. I have a quite simple self-hosted service:
class Program
{
static void Main(string[] args)
{
using (ServiceHost serviceHost = new ServiceHost(typeof(TestService)))
{
serviceHost.Open();
Console.WriteLine("Press <ENTER> to terminate service.");
Console.ReadLine();
serviceHost.Close();
}
Uri baseAddress = new Uri("net.tcp://localhost:8014/TestService.svc");
}
}
[ServiceContract]
public interface ITestService
{
[OperationContract]
string GetData(string data);
}
public class TestService: ITestService
{
public string GetData(string data)
{
Console.WriteLine(data);
Thread.Sleep(5000);
return "Ok";
}
}
The configuration part:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicHttpBinding" closeTimeout="00:02:00" openTimeout="00:02:00"
receiveTimeout="00:02:00" sendTimeout="00:02:00" maxBufferSize="2000000000"
maxReceivedMessageSize="2000000000" />
</basicHttpBinding>
<netTcpBinding>
<binding name="netTcpBinding" closeTimeout="00:02:00" openTimeout="00:02:00"
receiveTimeout="00:02:00" sendTimeout="00:02:00" listenBacklog="2000"
maxBufferSize="2000000000" maxConnections="1000" maxReceivedMessageSize="2000000000">
<security mode="None">
<transport protectionLevel="EncryptAndSign" />
</security>
</binding>
<binding name="TestServiceTcpEndPoint">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="CommonServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceThrottling maxConcurrentCalls="1000" maxConcurrentSessions="1000" maxConcurrentInstances="1000" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="ServiceLauncher.TestService" behaviorConfiguration="CommonServiceBehavior">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="netTcpBinding" name="TestServiceTcpEndPoint" contract="ServiceLauncher.ITestService" />
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicHttpBinding" name="TestServiceTcpEndPoint" contract="ServiceLauncher.ITestService" />
<endpoint address="mex" binding="mexHttpBinding" bindingName="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8014/TestService.svc"/>
<add baseAddress="http://localhost:1234/TestService.svc"/>
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
And I have a client which consumes this service in many threads with creating new instance for every thread (it is a requirement):
static void Main(string[] args)
{
for (int i = 0; i < 1000; i++)
{
Thread tr = new Thread(() =>
{
using (var service = new Test.TestServiceClient())
{
var result = service.GetData(i.ToString());
Console.WriteLine(string.Format("{0}: {1} {2}",
DateTime.Now,
result,
Thread.CurrentThread.ManagedThreadId));
}
});
tr.Start();
}
Console.ReadLine();
}
In this case after some requests client raises EndpointNotFoundException, TCP error code 10061, No connection could be made because the target machine actively refused it. The number of requests is different all the time, and it is not the server part because it still works in normal state. And I see it keeps recieving the requests, what is most strangest in this situation. What is also strange that it can make your client host "immortal" after the exception - so that you can't kill it by any mean, except of the reboot of the system. I'm pretty sure that the problem is in low socket level of the client, and it is somehow connected with such a large number of threads, but I didn't succeed in finding something which could explaine the problem.
Every time I've seen the error "No connection could be made because the target machine actively refused it." the problem has not been with the service. Its usually a problem reaching the service.
A couple suggestions:
Avoid using with WCF Proxies. You can pick from several reasonable work arounds.
Read my answer to WCF performance, latency and scalability. Other than starting threads the old fashioned way, its basically the same test app. The post describes all the client causes (I could find) that cause “No connection could be made because the target machine actively refused it” and offers different WCF, TCP, and thread pool settings that can be adjusted.
You could be hitting the internal limits on concurrent TCP/IP connections in windows. Have a look at this article and see if it helps:
http://smallvoid.com/article/winnt-tcpip-max-limit.html
Is it possible to call a WCF service from a universal application?
I added a service reference and the proxy was generated just fine.
But when creating a NetTcpBinding programmatically and passing that to the proxy's constructor the service model throws the exception PlatformNotSupported.
Both running the app in the simulator and on the local machine generates the same exception.
An exception of type 'System.PlatformNotSupportedException' occurred
in System.Private.ServiceModel.dll but was not handled in user code
"this operation is not supported"
EndpointAddress address = new EndpointAddress("net.tcp://test:9000/ServicesHost/PublishService");
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.None;
PublishingService.PublishClient proxy = new PublishingService.PublishClient(binding, address);
Does anybody have an example of a working WCF client in a UAP?
EDIT
It has something to do with the service being a duplex service!
The original contract:
[ServiceContract(CallbackContract = typeof(IPublishCallback))]
public interface IPublish { }
After removing the CallbackContract attribute the UAP client can create a connection, so basic WCF works.
So I guess it's better to rephrase the question.
Is it possible to create a duplex WCF client in a universal application?
edit servicemodel for the host
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="netTcpPublishService" openTimeout="00:00:10" receiveTimeout="infinite">
<reliableSession inactivityTimeout="24.20:31:23.6470000" enabled="true" />
<security mode="Transport">
<transport clientCredentialType="Windows" />
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehaviour">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="serviceBehaviour" name="PublishService.Publish">
<endpoint binding="mexHttpBinding" name="mexPublishService"
contract="IMetadataExchange" />
<endpoint address="PublishService" binding="netTcpBinding" bindingConfiguration="netTcpPublishService"
name="netTcpPublishService" contract="PublishService.IPublish" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8004/ServicesHost/PublishService" />
<add baseAddress="net.tcp://localhost:9004/ServicesHost/PublishService" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
Yes, it is possible. This is how i connect in a sample app i did a while ago:
using Tradeng.Srvc.Client.WinAppSimple.SrvcRefTradeng;
private InstanceContext instanceContext;
private TradengSrvcClientBase serviceProxy;
instanceContext = new InstanceContext(this);
serviceProxy = new TradengSrvcClientBase(instanceContext);
bool result = await serviceProxy.ConnectAsync();
if (result)
{
// connected...
}
I used the binding from the config file that is generated when you add a reference to your service.
This is what the app looks like. Cutting edge stuff.... :O)
https://www.youtube.com/watch?v=YSg6hZn1DpE
The service itself is running as a WebRole on Azure, by the way.
I have the followings:
In Competitions.svc:
<%# ServiceHost Language="C#" Debug="true" Service="MySite_WebSite.Pages.Client.CompetitionsSVC" CodeBehind="Competitions.svc.cs" %>
In ICompetitions.cs :
namespace MySite_WebSite.Pages.Client
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ICompetitions" in both code and config file together.
[ServiceContract(Name="CompetitionsSVC")]
public interface ICompetitions
{
[OperationContract]
[WebInvoke(
Method = "GET"
, RequestFormat = WebMessageFormat.Json
, ResponseFormat = WebMessageFormat.Json
, UriTemplate = "DoWork"
, BodyStyle=WebMessageBodyStyle.Wrapped
)]
Dictionary<DateTime, List<Competitions.Entry>> DoWork();
}
}
In Competitions.svc.cs :
namespace MySite_WebSite.Pages.Client
{
[DataContract]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class CompetitionsSVC : ICompetitions
{
#region ICompetitions Members
public Dictionary<DateTime, List<Competitions.Entry>> DoWork()
{
var c = new Competitions();
return c.GetMonthlyEntries(new Competitions.ParamGetMonthlyEntries()
{
Start = DateTime.Now.Date.AddMonths(-1)
, End = DateTime.Now.Date.AddMonths(2)
, UserLang = "fr-BE"
, ActiveLang = "fr-BE"
, IsExternal = false
});
}
#endregion
}
}
In Web.config:
<system.serviceModel>
<services>
<service name="MySite_WebSite.WS.WCF.SubsetMID">
<endpoint address=""
binding="wsHttpBinding"
contract="MySite_WebSite.WS.WCF.ISubsetMID" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
<service name="MySite_WebSite.Pages.Client.CompetitionsSVC">
<endpoint address=""
binding="webHttpBinding"
behaviorConfiguration="WebBehavior"
contract="MySite_WebSite.Pages.Client.ICompetitions" />
<endpoint address="mex"
binding="mexHttpBinding"
contract="IMetadataExchange" />
</service>
</services>
<bindings>
<wsHttpBinding>
<binding>
<security mode="None"/>
</binding>
</wsHttpBinding>
<netTcpBinding>
<binding name="NetTcpBinding_IServiceWCallBack" sendTimeout="00:10:00"
maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<readerQuotas maxStringContentLength="2147483647" />
<security mode="None" />
</binding>
<binding name="NetTcpBinding_IHandleSubset">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="WebBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment
multipleSiteBindingsEnabled="true"
aspNetCompatibilityEnabled="true"
/>
</system.serviceModel>
When I enter the url
localhost2/MySite_WebSite/Pages/Client/Competitions.svc/DoWork
, it doesn't work.
I have a breakpoint at the begining of the method, and I can see the method gets called twice, yet it doesn't return anything (I don't even think it send any HTTP code backs).
What did I do wrong?
Additional notes:
Entry is actually a "base class".
public class EntryCompetition : Entry
public class EntryEvent : Entry
In my code the dictionary actually contains EntryCompetition and EntryEvent instances.
Thanks for posting your code that definitely helps. But i think you're going to need to show a little more work, and some more concrete results on how your project is failing. But so as not to leave you helpless. I recommend looking at Fiddler
http://www.telerik.com/fiddler
It allows you to create Http requests and to see the responses inside of it's console. it is useful for seeing specifically what http response code your endpoint is returning, and allows you to modify your request through the composer window.
another helpful tip, would be to step all the way through your code, so you can point us to exactly what line is failing or what values are being set before your method completes.
Without more information, my best guess is you're code is throwing and most likely swallowing an exception. Or your methods or calls are setting null values that don't return the values you expect. Please reply once you've setup some further tests and updated your question if you are still having issues.
Ok, I solved the problem. I use a custom piece o code to serialize the dictionnary into a JSON string and I don't use DateTime objects as keys anymore.
Setup is mono latest stable on raspberry pi, polling data from service on server with .NET 4.5.
My configuration for server side:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="vlcBehaviour">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="ser.Serwys">
<endpoint address="net.tcp://192.168.56.1:9070/WindowService" binding="netTcpBinding" bindingConfiguration="AnonymousTcpBinding"
contract="Window.Service.IWindowServiceHost" />
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="AnonymousTcpBinding" receiveTimeout="00:00:01"
sendTimeout="00:00:01"
maxBufferSize="1000"
maxConnections="100"
maxBufferPoolSize="100"
listenBacklog="200"
maxReceivedMessageSize="1000">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
On client side:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="maxItems">
<dataContractSerializer maxItemsInObjectGraph="2147483646" />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="AnonymousTcpBinding" openTimeout="00:00:01" receiveTimeout="00:00:01" sendTimeout="00:00:01" maxBufferSize="1000" maxConnections="200" listenBacklog="200" maxReceivedMessageSize="1000">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint name="WindowService" address="net.tcp://192.168.56.1:9070/WindowService" binding="netTcpBinding" bindingConfiguration="AnonymousTcpBinding" contract="Window.Service.IWindowServiceHost" />
</client>
</system.serviceModel>
Service contract:
namespace Window.Service
{
[ServiceContract(
Namespace = "http://window",
Name = "WindowHost")]
public interface IWindowServiceHost
{
[OperationContract]
void Connect(int windowId);
[OperationContract]
void Disconnect();
[OperationContract]
DisplayData GetData();
}
}
Implementation of service:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Single)]
public class Serwys:IWindowServiceHost
Method that is called:
public DisplayData GetData()
{
Console.WriteLine("Called get data {0}:{1}:{2}",DateTime.Now.Minute,DateTime.Now.Second,DateTime.Now.Millisecond);
return new DisplayData
{
BoxesInProgress = 1,
};
}
Call to GetData from client takes more and more time, don't know why.
I have tested it on Ubuntu also, Mono is latest version here how I log it:
Stopwatch stp = new Stopwatch();
stp.Start();
var data = remote.GetData();
stp.Stop();
log.WarnFormat("Polling service took: {0} ms", stp.ElapsedMilliseconds);
Any explanations?? I think it is Mono bug, because setup is really basic no concurrency method called is dead simple, and polling times are going up.
I call it on timer elapse, and for test I call it every 10ms but it is growing even if I call it every second, but slower, and this only gives me more time before all crashes.