in WCF discovery example, i have server and client host. I need to start client host from server. how to do that.
in server, i have a method in discovery contract.
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
in client host, i point to the discovery contract.
Uri baseAddress = new Uri(string.Format("http://{0}:8000/discovery/scenarios/Myservice/",System.Net.Dns.GetHostName(), Guid.NewGuid().ToString()));
Console.WriteLine(baseAddress);
using (ServiceHost serviceHost = new ServiceHost(typeof(WCFDiscoveryContract.Service1), baseAddress))
{
serviceHost.AddServiceEndpoint(typeof(WCFDiscoveryContract.IService1), new WSHttpBinding(), string.Empty);
serviceHost.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
serviceHost.AddServiceEndpoint(new UdpDiscoveryEndpoint());
serviceHost.Open();
Console.WriteLine("Press to terminate service.");
Console.ReadLine();
}
in server i have another solution which searches for a discovery contract and calls the method GetData().
ServiceClient.ServiceReference1.Service1Client client = new ServiceClient.ServiceReference1.Service1Client();
client.Endpoint.Address = serviceAddress;
string ss = client.GetData(1);
For calling GetData method, client host should be running. Is there any way to start the client host from the server.
Related
I'm building an ASP.NET Core web API project.
Service runs sometimes, sometimes fails.
I get 90% successful results.
There was no endpoint listening at (url) that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
The binding is as follows
The service should work improperly. By default, the invocation sent by a proxy preferentially uses the service URI generated in the reference.cs file.
We should change it to the practical service URL.
https://vabqia96vm:21011/
Furthermore, the service communicates with the client is protected by the transport layer security. We should trust the server’s certificate before sending an invocation or add a procedure of SSL authentication to ignore validating the server's certificate.
ServiceReference1.ServiceClient client = new ServiceClient();
//ignore the SSL authentication.
client.ClientCredentials.ServiceCertificate.SslCertificateAuthentication=new System.ServiceModel.Security.X509ServiceCertificateAuthentication()
{
CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None,
RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck
};
var result = client.TestAsync();
Console.WriteLine(result.Result);
Also, we could call the service by using ChannelFactory.
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("https://vabqia969vm:21011");
BasicHttpsBinding binding = new BasicHttpsBinding();
binding.Security.Mode = BasicHttpsSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
ChannelFactory<IService> factory = new ChannelFactory<IService>(binding, new EndpointAddress(uri));
factory.Credentials.ServiceCertificate.SslCertificateAuthentication=new System.ServiceModel.Security.X509ServiceCertificateAuthentication()
{
CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None,
RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck
};
var client = factory.CreateChannel();
var result = client.Test();
Console.WriteLine(result);
}
}
//the service contract is shared between the server-side and the client-side.
[ServiceContract]
public interface IService
{
[OperationContract]
string Test();
}
Feel free to let me know if there is anything I can help with.
I have recently started a new job where WCF services are being used. I have used them in the past and am comfortable with them but from what I can recall if the client does not close the connection it has the ability to bring your service down entirely. I am aware of the proper procedure for closing the connections but if the responsibility is on the client, they may not follow the same practices and potentially have the ability to bring the service down. Is there any other way of handling the closing of the connections so that it is not reliant on the client doing the right thing? It seems odd that anyone who has access to your service has the ability to bring it down with such ease...
Thank you very much for any insights!
One option is to use session time out in the server. This actually faults the client channel.
There are only really three ways in which a session can terminated:
1) The client closes the proxy
2) The service's receiveTimeout is exceeded before the client sends another request
3) The service throws a non-fault exception which will fault the channel and so terminate the session
If you don't want the client involved then you only have 2 and 3 neither of which end well for the client - they will get an exception in both situation on the next attempt to talk to the service.
You could use Duplex messaging and get the service to notify the client that its requires session termination - the client then gets an opportunity to close down the proxy gracefully but this is a cooperative strategy
Or you need to use duplex (but still the client will have to call the service).
Here is some important points of the service implementation:
a: Use a static dictionary to keep the Client’s IP and callback channel. Before writing on the share object, lock the object.
b: Gets the IP address of the client using the GetAddressAsString method. You can get the IP of the client from the incoming message. The following statement shows how can we get the IP adddress of the Client in WCF:
RemoteEndpointMessageProperty clientEndpoint = OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
String ipAddress = clientEndpoint.Address;
If you are using the namepipe binding, you will not get the RemoteEndpointMessageProperty.
c: When the client creates the proxy of the service, it will call StartingService method immediately. Inside the StartingService method, I am keeping the callback channel of the client and current instance into the dictionary.
d: When the user of WCF service wants to disconnect a client, he/she will call the Disconnect method with the IP Address of the client.
e: The Disconnect method uses the IP Address to get the callback channel of the client and associate service instance of the client from the dictionary. Eventually, it notifies the client by using callback channel and close the incoming channel.
Here is the implementation through code:
[ServiceContract(CallbackContract=typeof(INotifyClientCallback),SessionMode=SessionMode.Required)]
public interface IService1
{
[OperationContract]
bool StartingService();
}
public interface INotifyClientCallback
{
[OperationContract(IsOneWay = true)]
void Disconnecting();
}
INotifyClientCallback interface for Callback.
Step 2: Implementation of the Contact:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class Service1 : IService1
{
private static readonly Dictionary subscribers = new Dictionary();
public static event EventHandler onClientAdded;
///
/// Returns the IP Address of the Client
///
///
public string GetAddressAsString()
{
if (!OperationContext.Current.IncomingMessageProperties.ContainsKey(RemoteEndpointMessageProperty.Name))
{
return "127.0.0.1";
}
RemoteEndpointMessageProperty clientEndpoint =
OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty;
return clientEndpoint.Address;
}
public bool StartingService()
{
//Get the callback reference
INotifyClientCallback callback = OperationContext.Current.GetCallbackChannel();
string IPAddress = GetAddressAsString();
lock (subscribers)
{
if (!subscribers.ContainsKey(IPAddress))
{
subscribers[IPAddress] = new CommunicationStore()
{ NotifyCallback = callback,
IService = OperationContext.Current.InstanceContext
};
if (onClientAdded != null)
{
onClientAdded(IPAddress, null);
}
}
}
return true;
}
public static void Disconnect(string ipAddress)
{
if (subscribers.ContainsKey(ipAddress))
{
CommunicationStore com = subscribers[ipAddress];
if (((ICommunicationObject)com.NotifyCallback).State == CommunicationState.Opened)
{
try
{
//fires the callback method
com.NotifyCallback.Disconnecting();
com.IService.IncomingChannels.FirstOrDefault().Close();
}
catch (Exception)
{
throw;
}
}
}
}
}
public class CommunicationStore
{
public InstanceContext IService { get; set; }
public INotifyClientCallback NotifyCallback { get; set; }
}
I have created WCF service project.
It has following content in SVC file.
<%# ServiceHost Service="Deepak.BusinessServices.Implementation.ApiImplementation"
Factory="Deepak.BusinessServices.Implementation.CustomServiceHostFactory"%>
SVC reference
http://localhost/DeepakGateway/Service.svc
Service is UP and WSDL generated. Now I want to host this service as Windows Service.
How can I do it?
I have created "Windows Service" Project ans have following code.
protected override void OnStart(string[] args)
{
if (m_Host != null)
{
m_Host.Close();
}
Uri httpUrl = new Uri("http://localhost/DeepakGateway/Service.svc");
m_Host = new ServiceHost
(typeof(?????? WHAT TO FILL HERE?), httpUrl);
//Add a service endpoint
m_Host.AddServiceEndpoint
(typeof(?????? WHAT TO FILL HERE?), ), new WSHttpBinding(), "");
//Enable metadata exchange
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
m_Host.Description.Behaviors.Add(smb);
//Start the Service
m_Host.Open();
}
You need to add the type of the class that implements your service contract in the ServiceHost constructor, and type of the service contract in your AddServiceEndpoint
Assuming your service implementation class looks something like this:
namespace Deepak.BusinessServices.Implementation
{
public class ApiImplementation : IApiImplementation
{
....
}
}
then you need:
m_Host = new ServiceHost(typeof(ApiImplementation), httpUrl);
m_Host.AddServiceEndpoint(typeof(IApiImplementation), new WSHttpBinding(), "");
the service host needs to know what (concrete) type of service class to host
the endpoint needs to know what service contract (interface) it exposes
I've created ASP.NET application and added simple WCF service to it. The ASP.NET application is host for WCF service. The service is running.
The service looks as follows:
[ServiceContract]
public interface IService1
{
[OperationContract]
string DoWork(string text);
}
public class Service1 : IService1
{
public string DoWork(string text)
{
return text.ToUpper();
}
}
On the client side is console application that should invoke WCF service dynamically. I use following code:
WSHttpBinding binding = new WSHttpBinding(SecurityMode.None);
IChannelFactory<IRequestChannel> factory = binding.BuildChannelFactory<IRequestChannel>(
new BindingParameterCollection());
factory.Open();
EndpointAddress address = new EndpointAddress("http://localhost:3929/Service1.svc");
IRequestChannel irc = factory.CreateChannel(address);
using (irc as IDisposable)
{
irc.Open();
XmlReader reader = XmlReader.Create(new StringReader(
#"<DoWork xmlns='http://tempuri.org/'>
<composite xmlns:a='http://www.w3.org/2005/08/addressing'
xmlns:i='http://www.w3.org/2001/XMLSchema-instance'>
<a:StringValue>aaaa</a:StringValue>
</composite>
</DoWork>"));
Message m = Message.CreateMessage(MessageVersion.Soap12,
"http://tempuri.org/IService1/DoWork", reader);
Message ret = irc.Request(m);
reader.Close();
Console.WriteLine(ret);
}
//close the factory
factory.Close();
But, it crashes at this line:
Message ret = irc.Request(m);
with following error:
The message version of the outgoing message (Soap12 (http://www.w3.org/2003/05/soap-envelope) AddressingNone (http://schemas.microsoft.com/ws/2005/05/addressing/none)) does not match that of the encoder (Soap12 (http://www.w3.org/2003/05/soap-envelope) Addressing10 (http://www.w3.org/2005/08/addressing)). Make sure the binding is configured with the same version as the message.
Does anybody know what I'm doing wrong?
Thank you in advance.
Message.CreateMessage(MessageVersion.Soap12,
Instead of the MessageVersion enum value Soap12, you need to specify Soap12Addressing10 to match your binding.
is it possible to call a service operation at a wcf endpoint uri with a self hosted service?
I want to call some default service operation when the client enters the endpoint uri of the service.
In the following sample these uris correctly call the declared operations (SayHello, SayHi):
- http://localhost:4711/clerk/hello
- http://localhost:4711/clerk/hi
But the uri
- http://localhost:4711/clerk
does not call the declared SayWelcome operation. Instead it leads to the well known 'Metadata publishing disabled' page. Enabling mex does not help, in this case the mex page is shown at the endpoint uri.
private void StartSampleServiceHost()
{
ServiceHost serviceHost = new ServiceHost(typeof(Clerk), new Uri( "http://localhost:4711/clerk/"));
ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(typeof(IClerk), new WebHttpBinding(), "");
endpoint.Behaviors.Add(new WebHttpBehavior());
serviceHost.Open();
}
[ServiceContract]
public interface IClerk
{
[OperationContract, WebGet(UriTemplate = "")]
Stream SayWelcome();
[OperationContract, WebGet(UriTemplate = "/hello/")]
Stream SayHello();
[OperationContract, WebGet(UriTemplate = "/hi/")]
Stream SayHi();
}
public class Clerk : IClerk
{
public Stream SayWelcome() { return Say("welcome"); }
public Stream SayHello() { return Say("hello"); }
public Stream SayHi() { return Say("hi"); }
private Stream Say(string what)
{
string page = #"<html><body>" + what + "</body></html>";
return new MemoryStream(Encoding.UTF8.GetBytes(page));
}
}
Is there any way to disable the mex handling and to enable a declared operation instead?
Thanks in advance, Dieter
Did you try?
[OperationContract, WebGet(UriTemplate = "/")]
Stream SayWelcome();
UPDATE:
Not sure why it is not working for you, I have a self hosted WCF service with the following service contract:
[ServiceContract]
public interface IDiscoveryService {
[OperationContract]
[WebGet(BodyStyle=WebMessageBodyStyle.Bare, UriTemplate="")]
Stream GetDatasets();
The only difference I can see is that I use WebServiceHost instead of ServiceHost.