implementing routing into my wcf service - wcf

I have clients that upload files to my server using a wcf service with streaming. The code on the client is something like this (omitting some details):
NetTcpBinding binding = new NetTcpBinding();
EndpointAddress address = new EndpointAddress("net.tcp://" + ipAddress + ":5000/DataUploader");
ChannelFactory<IDataUploader> channel = new ChannelFactory<IDataUploader>(binding, address);
IDataUploader uploader = channel.CreateChannel();
try
{
uploader.Upload(msg);
ConsoleText.Record("The file was sent...\n");
}
catch (CommunicationException)
{
ConsoleText.Record("The file was not sent...\n" + "Interrupted connection...\n");
}
finally
{
uploadStream.Close();
((IClientChannel)uploader).Close();
}
I want to implement a routing service between server and client, the routing service would be something like this:
private static void ConfigureRouterViaCode(ServiceHost serviceHost)
{
string clientAddress = "http://localhost:5000/DataUploader";
string routerAddress = "http://localhost:5000/RouterService";
Binding routerBinding = new WSHttpBinding();
Binding clientBinding = new WSHttpBinding();
serviceHost.AddServiceEndpoint(typeof(IRequestReplyRouter), routerBinding, routerAddress);
ContractDescription contract = ContractDescription.GetContract(typeof(IRequestReplyRouter));
ServiceEndpoint client = new ServiceEndpoint(contract, clientBinding, new EndpointAddress(clientAddress));
RoutingConfiguration rc = new RoutingConfiguration();
List<ServiceEndpoint> endpointList = new List<ServiceEndpoint>();
endpointList.Add(client);
rc.FilterTable.Add(new MatchAllMessageFilter(), endpointList);
serviceHost.Description.Behaviors.Add(new RoutingBehavior(rc));
}
It's confused how I can connect my client to the routing service first. Is this a good approach?? Thanks.

your approach is correct. On the client, change the address pointing to the routing service, leaving all other settings as they were. I suggest you study http://msdn.microsoft.com/en-us/library/ee517423.aspx or find some demo routing implementations.

Related

How to get the WCF service address from DynamicEndpoint

I Crreated a DynamicEndpoint to find the WCF service automatically.
namespace Client
{
class Program
{
static void Main(string[] args)
{
DynamicEndpoint dynamicEndpoint = new DynamicEndpoint(ContractDescription.GetContract(typeof(ICalculator)), new NetTcpBinding());
using (ChannelFactory<ICalculator> channelFactory = new ChannelFactory<ICalculator>(dynamicEndpoint))
{
ICalculator caculate = channelFactory.CreateChannel();
Console.WriteLine("x + y = {2} when x = {0} and y = {1}", 4, 9, caculate.Add(4, 9));
Console.WriteLine("Find service, the service address is: " + dynamicEndpoint.Address.Uri);
}
Console.Read();
}
}
}
The problem is when I try to print the service address, the return value is
http://schemas.microsoft.com/discovery/dynamic
That's not the real service address I published.
1. How to get the real service address?
2. If there are multiple services available, which one will DynamicEndpoint choose? Can I get the address array or list?
As far as I know, we could not get the actual use endpoint in client. except that we use the OperationContext object,which provides access to the execution context of a service method.
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.operationcontext?redirectedfrom=MSDN&view=netframework-4.7.2
For example, we could refer to the following code to get the actual endpoint.
Server.
public string GetAddress()
{
OperationContext oc = OperationContext.Current;
string result=oc.Channel.LocalAddress.Uri.ToString();
return result;
}
Client.
ChannelFactory<IService> factory = new ChannelFactory<IService>(dynamicEndpoint);
IService sv = factory.CreateChannel();
Console.WriteLine(sv.GetAddress());
Besides,I don't think dynamic endpoint could list the endpoints that have been found. Dynamic Endpoint merge service discovery with service invokation. when a service is invoked using a dynamic endpoint, it will depend on the FindCriteria property to find the service endpoint and then invokes it.
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.discovery.dynamicendpoint?view=netframework-4.7.2
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.discovery.findcriteria?view=netframework-4.7.2

Resolve a URI to an EndpointAddress

I currently have a WCF client that is able to do ad-hoc service discovery to find (unknown) services running on the local subnet. I would like to implement a way for the user to specify a service endpoint to use by entering a URI into a text box, and for the client to resolve this URI to an EndpointAddress, and in the process gather additional metadata about the service. Namely, I need to gather the EndpointIdentity and additional data exposed in the Extensions property of the EndpointDiscoveryBehavior.
I am trying to achieve this by using DiscoveryClient.Resolve(), but I am only receiving null for the ResolveResponse.EndpointDiscoveryMetadata property.
String Address = "net.tcp://machine-name:12345/MyService"
DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
var criteria = new ResolveCriteria()
{
Address = new EndpointAddress(Address)
};
var result = discoveryClient.Resolve(criteria);
//scv is null here.....
var svc = result.EndpointDiscoveryMetadata;
I've found a lot of information out there regarding DiscoveryClient.Find(), but not so much about DiscoveryClient.Resolve().
So my questions are:
Is this the intended use of DiscoveryClient.Resolve()?
Is MetadataResolver more appropriate here?
How does one resolve an URI to a EndpointAddress and obtain other metadata?
I think you are trying to replicate functionality of svcutil.exe. In that case you may have to resolve the mex endpoint first and query service metadata from that endpoint (IMetaDataExchange). The SPN identity should be in the metadata.
Also see reference http://msdn.microsoft.com/en-us/library/ms733130.aspx
I achieved what I wanted to do like so:
String Address = "net.tcp://machine-name:12345/MyService"
DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
var endpoint = new EndpointAddress(new Uri(Address));
var criteria = new ResolveCriteria()
{
Address = endpoint
};
var result = discoveryClient.Resolve(criteria);
var mexClient = new MetadataExchangeClient(MetadataExchangeBindings.CreateMexTcpBinding());
var contracts = new List<ContractDescription>() { ContractDescription.GetContract(typeof(RuntimeService.Services.IWorkflowService)) };
var metaResult = MetadataResolver.Resolve(contracts, endpoint, mexClient);
var svc = metaResult.First();
I am able to get to the extension data through result and svc provides me with the correct EndpointAddress complete with the correct identity.
Thanks to #YK1 for pushing me in the right direction.

Hold TcpClient Connection with WCF

Hallo
I need a Tcp connection to a server alive in backgrond and applications will send data with this connection.I searched around and found WCF singleton is apporiate for this task
here is a code snippet that i use below
my question is that the good way and any problem can be with this?
string hostAddress = string.Empty;
try
{
srvHost = new ServiceHost(typeof(ControllerClass));
NetTcpBinding netTcpBinding = new NetTcpBinding(SecurityMode.None);
netTcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.None;
netTcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.None;
netTcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
srvHost.AddServiceEndpoint(typeof(IControllerContract), netTcpBinding, hostAddress);
srvHost.Credentials.WindowsAuthentication.AllowAnonymousLogons = true;
ServiceThrottlingBehavior serviceThrottlingBehavior = new ServiceThrottlingBehavior();
serviceThrottlingBehavior.MaxConcurrentCalls = 1000;
serviceThrottlingBehavior.MaxConcurrentInstances = 1000;
serviceThrottlingBehavior.MaxConcurrentSessions = 1000;
srvHost.Description.Behaviors.Add(serviceThrottlingBehavior);
srvHost.Open();
}
catch (System.TimeoutException timeoutEx)
{
Thread.Sleep(1000);
ReOpenHostConnection();//initialize again Controller Class
}
catch (Exception ex)
{
Trace.WriteLine(string.Format("cannot start Service Ex:{0}", ex.ToString()), TraceEventType.Error.ToString());
}
//Controller Class Initialize Code Snippet
TcpClient iTcpClient = new TcpClient();
iTcpClient.Connect(serverIP, serverPort);
networkStream = iTcpClient.GetStream();
aSychDataByte = new byte[iTcpClient.ReceiveBufferSize];
networkStream.BeginRead(aSychDataByte, 0, incommTcpClient.ReceiveBufferSize, ReadAsych, null);
Why do you combine TcpClient with WCF? If you need low level Tcp communication build client and server based on low level Tcp classes. If you need service and you don't bother with message formats use WCF.
For your problem. You don't need singleton. You just need the connection to be opened. For this you need to create WCF proxy instance (open channel) on the client and call the service. It will create connection to service instance wich will live until you close the client proxy, until your service host stops working or until timeout (10 minutes of client inactivity by default).

WCF: Why does passing in a remote endpoint fail?

The problem I am having connecting a wcf client application to a host running on a separate machine is documented in a question previously asked:
WCF: Why does passing in a remote endpoint fail?
However, the solution provided here says you need to use a SpnEndpointIdentity with an empty string. Since my code doesn't look anything like the case in the example I have referenced, I need to know what to do with the SpnEndpointIdentity object I have created.
I have a ChannelFactory upon which I call Create channel, passing in an EndpointAddress:
public override void InitialiseChannel()
{
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var address = new EndpointAddress(EndpointName);
Proxy = ChannelFactory.CreateChannel(address);
}
(NB: ChannelFactory is of type IChannelFactory, where T is the service contract interface)
So what do I do with spnEndpointIdentity? I can't pass it to CreateChannel.
Or perhaps I can use it somehow when I create the channel factory:
private ChannelFactory<T> CreateChannelFactory()
{
var binding = new NetTcpBinding
{
ReaderQuotas = { MaxArrayLength = 2147483647 },
MaxReceivedMessageSize = 2147483647
};
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var channelFactory = new ChannelFactory<T>(binding);
return channelFactory;
}
Again, I can't pass it into the constructor, so what do I do with it?
Thanks.
You almiost got it.
What you're missing is that you associate the EndpointIdentity with the EndpointAddress, and then provide that to CreateChannel():
SpnEndpointIdentity spnEndpointIdentity = new SpnEndpointIdentity("");
var address = new EndpointAddress(EndpointName, spnEndpointIdentity);

How to programmatically turn off security in WCF

I've been working with WCF the last two days and it was going very well with the server and client both on my development machine. Now that I am trying to do some distributed testing with the client on another machine on the network I've started running into problems. Right now the error I am getting is:
The message with Action 'http://tempuri.org/IWindowUpdateContract/UpdateWindowFrames' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
As this is already a massive learning experience (I haven't done any remoting, RPC, et al before) I want to continue developing the learning tool and revisit security when I'm finished (I have no intention of building anything that will actually get used without the proper security best practices).
Notes:
I don't have a configuration file setup for WCF - I'm doing everything programmatically.
My network is not part of a domain so the default security settings were not working for me (using net.tcp).
I'm using '.Net 3.5'.
My server is created like this:
var svh = new ServiceHost(_serviceImplementation);
var binding = new NetTcpBinding();
binding.ReaderQuotas.MaxArrayLength = 2000000;
binding.Security.Mode = SecurityMode.None;
binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
binding.Security.Transport.ProtectionLevel = ProtectionLevel.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.None;
svh.AddServiceEndpoint(_serviceInterface, binding, string.Format("net.tcp://{0}:{1}", _endPoint.Address, _endPoint.Port));
_stopFlag = new AutoResetEvent(false);
svh.Open();
_stopFlag.WaitOne();
And my client is created like this:
var binding = new NetTcpBinding();
binding.ReaderQuotas.MaxArrayLength = 2000000;
binding.Security.Mode = SecurityMode.None;
binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
binding.Security.Transport.ProtectionLevel = ProtectionLevel.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.None;
var scf = new ChannelFactory<IUserInputContract>(binding, "net.tcp://192.168.0.42:8001");
_uiUpdateServer = scf.CreateChannel();
And my contract (which is just in a class library that is added as a reference to both the client and server) is:
[ServiceContract(ProtectionLevel = ProtectionLevel.None)]
public interface IWindowUpdateContract {
[OperationContract]
void UpdateWindowFrames(WindowFrame frame);
[OperationContract]
void WindowHasClosed(IntPtr hwnd);
}
I feel that the binding and contract setup I have done should make them identical and I shouldn't have this problem (and security should be turned off). I just don't know where to go now.
I agree with you - it would seem that the security settings for both server and client are identical.
A note on the side: once you do:
binding.Security.Mode = SecurityMode.None;
I don't think you need to specify any more setting on the "binding.Security" object or below - those extra lines after that are not needed.
What caught my eye is your service contract:
[ServiceContract(ProtectionLevel = ProtectionLevel.None)]
public interface IWindowUpdateContract
{
[OperationContract]
void UpdateWindowFrames(WindowFrame frame);
[OperationContract]
void WindowHasClosed(IntPtr hwnd);
}
Those operations don't return anything - that's unusual. The default behavior for a WCF service is Request/Response - you send a request and get back a response.
Either make them return something (a status or such; like a string, an int), or then you'll need to mark them as "one-way" calls so that WCF knows not to expect anything back:
[ServiceContract(ProtectionLevel = ProtectionLevel.None)]
public interface IWindowUpdateContract
{
[OperationContract(IsOneWay=true)]
void UpdateWindowFrames(WindowFrame frame);
[OperationContract(IsOneWay=true)]
void WindowHasClosed(IntPtr hwnd);
}
Marc