How to cleanly restart a WCF under new address? - c#

I have a WPF C# application hosting a WCF service. I have a routine to start the service, closing it if it already exists. WebServce is a property of type ServiceHost:
public void Start()
{
try
{
var certificate = new X509Certificate2(certpath, "");
String uri = "net.tcp://" + WCFAddress + "/MyService";
Uri baseaddress = new Uri(uri);
if (WebService != null) {
try {
WebService.Close();
} catch (Exception) {
WebService.Abort();
}
}
WebService = new ServiceHost(MessageProvider, baseaddress);
WebService.Credentials.ServiceCertificate.Certificate = certificate;
WebService.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
WebService.AddServiceEndpoint(typeof(IMessageService), binding, baseaddress);
WebService.Open();
}
catch (Exception e)
{
//exception handling
}
}
This works fine on startup. It also works fine if I change the port number (in WCFAddress) and call the routine again. It also works if I change the address on the host computer and call it with the new IP address. However, if I change the IP address to an invalid one, the service goes into a Faulted state with the error:
e = {"A TCP error (10049: The requested address is not valid in its context) occurred while listening on IP Endpoint=192.168.1.4:5000."}
The Close() call in the above doesn't raise an exception.
If I then change the IP address to the correct one and call again, I get the same error, with the same old incorrect address, even though I passed it the correct one. Also this time the Close() call raises an error due to the Faulted state, which results in the Abort() call.
I thought the Abort() call would allow the service to then be recreated? Why is it giving me an error about the old address when I'm trying to create the service with a new one? It's like it's hanging onto the old ServiceEndpoint?

Abort(). Then just initiate a new instance with the new address.. then Open().

Related

WCF Application hosted service Faulted State exception?

No idea what's going on. Simple application hosted service. Ran fine on server A. Copied everything over to server B...and suddenly won't launch.
Any tips? Ideas? I'll happily provide more info. Thanks for any help.
Error message:
The communication object, System.ServiceModel.ServiceHost, cannot be
used for communication because it is in the Faulted state.
Code (FAILS AT HOST.OPEN()_
static void Main(string[] args)
{
try
{
Uri baseAddress = new Uri("http://localhost:8080/Brp");
Uri mexUri = new Uri("http://localhost:8080/Brp/mex");
// Create the ServiceHost.
using (ServiceHost host = new ServiceHost(typeof(BBService), baseAddress))
{
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetUrl = mexUri;
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
host.AddServiceEndpoint(ServiceMetadataBehavior.MexContractName, MetadataExchangeBindings.CreateMexHttpBinding(), "mex");
BasicHttpBinding binding = new BasicHttpBinding();
binding.MaxReceivedMessageSize = int.MaxValue;
binding.Security.Mode = BasicHttpSecurityMode.None;
host.AddServiceEndpoint(typeof(IBService), binding, "");
// Enable metadata publishing.
var behavior = host.Description.Behaviors.Find<ServiceDebugBehavior>();
behavior.IncludeExceptionDetailInFaults = true;
host.Open();
Console.ReadLine();
// Close the ServiceHost.
host.Close();
}
} catch (Exception excep)
{
writeMessage("EXCEPTION!!! - " + excep.Message);
}
In case anyone else runs into this do: Right-click -> Run as administrator
You have to follow certain rules while working with Message contract
1. When using Message contract type as parameter, Only one parameter can be used in servicie Operation
[OperationContract]
void SaveEmployeeDetails(EmployeeDetails emp);
2. Service operation either should return Messagecontract type or it should not return any value
[OperationContract]
EmployeeDetails GetEmployeeDetails();
3. Service operation will accept and return only message contract type. Other data types are not allowed.
[OperationContract]
EmployeeDetails ModifyEmployeeDetails(EmployeeDetails emp);
Note: If a type has both Message and Data contract, service operation will accept only message contract.

Launching WCF only if not already running

I have my WCF in my service References and when I run my WCF at the same time as I run my WPF using visual studio run multi projects then it all works fine, however I am now connecting multiple clients and if they start at the same time before no data is entered then it works. If one starts enters data then the other starts then the entered data is wiped. I have tried having it so it will start by running a host from my WPF. Unfortunately I get an error saying httpGetEnabled needs to be false, if this is false then I cannot update my service reference as it says there is an access issue. The code I have used for trying to run the host is.
try
{
ServiceHost host;
Service1.Service1Client service = new Service1.Service1Client();
string baseAddress = "http://localhost:59849/Service1.svc";
host = new ServiceHost(typeof(Service1.Service1Client));
host.AddServiceEndpoint(typeof(Service1.IService1),new BasicHttpBinding(), baseAddress);
host.Open();
wcfHostId = wcf.generateId();
textBox5.Text = "" + wcfHostId;
button5.IsEnabled = false;
}
catch (Exception ex)
{
MessageBox.Show("Error = " + ex.Message);
}
Edit
So basically what I was saying is when I was self hosting a WPF and a new client connected it was wiping all the variables stored inside the service. And I was enquiring was it because of the way I was hosting the service?
The error was the way I was trying to discover the WCF and didn't add a discovery endpoint.
host.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
host.AddServiceEndpoint(new UdpDiscoveryEndpoint());
needed to be added to the host part and the client code in the end was
var ep = "http://" + System.Net.Dns.GetHostName() + ":8732/DatabaseTransfer/Service1/";
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.None;
binding.SendTimeout = new System.TimeSpan(0, 1, 30);
ChannelFactory<IService1> wcfFactory = new ChannelFactory<IService1>(binding, new EndpointAddress(ep));
IService1 wcf = wcfFactory.CreateChannel();

How to work with WCF wsHttpBinding and SSL?

I need to develop a WCF Hosted in a console app WebService.
I made it work using the Mutual Certificate (service and client) method using SecurityMode.Message.
But now i need to change the Security Mode to SecurityMode.Transport and use the wsHttpBinding with SSL. I made this code to host the service but i cannot get the wsdl with the browser, or execute some webmethod in the console app client.
static void Main()
{
var httpsUri = new Uri("https://localhost:8089/HelloServer");
var binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var host = new ServiceHost(typeof(WcfFederationServer.HelloWorld), httpsUri);
host.AddServiceEndpoint(typeof(WcfFederationServer.IHelloWorld), binding, "", httpsUri);
var mex = new ServiceMetadataBehavior();
mex.HttpsGetEnabled = true;
host.Description.Behaviors.Add(mex);
// Open the service.
host.Open();
Console.WriteLine("Listening on {0}...", httpsUri);
Console.ReadLine();
// Close the service.
host.Close();
}
The service is up, but i cannot get nothing on the https://localhost:8089/HelloServer.
On fiddler the get request via browser shows me this message:
fiddler.network.https> HTTPS handshake to localhost failed. System.IO.IOException
What im missing here?
Thanks
EDIT:
The Console Application Client Code
static void Main()
{
try
{
var client = new HelloWorldHttps.HelloWorldClient();
client.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.TrustedPeople,
X509FindType.FindBySubjectName,
"www.client.com");
Console.WriteLine(client.GetData());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
Getting this error:
Could not establish trust relationship for the SSL/TLS secure channel
When it comes to the service, you need to map the certificate to the specific port as described here
http://msdn.microsoft.com/en-us/library/ms733791(v=vs.110).aspx
As for the client, you need to skip the verification of certificate properties like valid date, the domain by relaxing the certificate acceptance policy. An easiest way would be to accept any certiticate
ServicePointManager.ServerCertificateValidationCallback = (a,b,c,d) => true
You can finetune the acceptance callback according to the docs to best fit your needs.

Changing from BasicHTTPBinding to BasicHTTPSBinding not working

I have a WCF service app and client app that I have written. I have been using BasicHTTPBinding on both ends.
To be clear: This is NOT a web application. The service is self-hosted in a PC application (NOT IIS). All configuration of the endpoints on both the client and server are done in code, not in app.config. In fact, to make sure, I have commented out the entire System.Servicemodel sections of app.config for both the client and server applications, and everything works great. This is all using BasicHTTPBinding.
Now, I want to change everything to use HTTPS instead of HTTP, for security reasons. I have changed what I thought I needed to change in my code, but it's not working.
Original Code (This works fine!)
Server:
BasicHttpBinding _binding;
Service1 _generalService;
ServiceHost _generalHost;
_binding = new BasicHttpBinding();
_binding.MaxReceivedMessageSize = 1000000;
_generalService = new Service1(_dbConnParams, _imagePath, _registrationProvider, LicenseCount);
_generalHost = new ServiceHost(_generalService, new Uri("http://localhost:8000"));
_generalHost.AddServiceEndpoint(typeof(IService1), _binding, "Service1");
_generalHost.Open();
Client:
internal static ToOrson.IService1 SetupService(string ServerAddress, TimeSpan? timeout = null)
{
var myBinding = new BasicHttpBinding();
myBinding.MaxReceivedMessageSize = 1000000;
if (timeout != null)
myBinding.SendTimeout = (TimeSpan)timeout;
string fullAddress = string.Format("http://{0}:8000/Service1", ServerAddress);
var myEndpoint = new EndpointAddress(fullAddress);
var myChannelFactory = new ChannelFactory<ToOrson.IService1>(myBinding, myEndpoint);
return myChannelFactory.CreateChannel();
}
private async void TestButton_Click(object sender, RoutedEventArgs e)
{
var TestService = MainWindow.SetupService(ServerBox.Text);
try
{
await TestService.TestConnectionAsync();
}
catch (Exception connException)
{
MessageBox.Show("Connection failed: " + connException.Message, "Error");
return;
}
MessageBox.Show("Connection succeeded!", "Success");
}
Code modified for HTTPS (This does NOT work)
Server:
BasicHttpsBinding _binding;
Service1 _generalService;
ServiceHost _generalHost;
_binding = new BasicHttpsBinding();
_binding.MaxReceivedMessageSize = 1000000;
_generalService = new Service1(_dbConnParams, _imagePath, _registrationProvider, LicenseCount);
_generalHost = new ServiceHost(_generalService, new Uri("https://localhost:8000"));
_generalHost.AddServiceEndpoint(typeof(IService1), _binding, "Service1");
_generalHost.Open();
Client:
internal static ToOrson.IService1 SetupService(string ServerAddress, TimeSpan? timeout = null)
{
var myBinding = new BasicHttpsBinding();
myBinding.MaxReceivedMessageSize = 1000000;
if (timeout != null)
myBinding.SendTimeout = (TimeSpan)timeout;
string fullAddress = string.Format("https://{0}:8000/Service1", ServerAddress);
var myEndpoint = new EndpointAddress(fullAddress);
var myChannelFactory = new ChannelFactory<ToOrson.IService1>(myBinding, myEndpoint);
return myChannelFactory.CreateChannel();
}
private async void TestButton_Click(object sender, RoutedEventArgs e)
{
var TestService = MainWindow.SetupService(ServerBox.Text);
try
{
await TestService.TestConnectionAsync();
}
catch (Exception connException)
{
MessageBox.Show("Connection failed: " + connException.Message, "Error");
return;
}
MessageBox.Show("Connection succeeded!", "Success");
}
Basically, I just changed BasicHTTPBinding to BasicHTTPSBinding and HTTP:// to HTTPS:// in all places in the code.
Here is the exception that I'm getting:
System.ServiceModel.CommunicationException "An error occurred while
making the HTTP request to https://localhost:8000/Service1. This could
be due to the fact that the server certificate is not configured
properly with HTTP.SYS in the HTTPS case. This could also be caused by
a mismatch of the security binding between the client and the server."
This exception is occurring on the client, on the call to TestService.TestConnectionAsync() (which is one of my OperationContracts).
Here are the inner exceptions:
System.Net.WebException "The underlying connection was closed: An
unexpected error occurred on a send."
System.IO.IOException "Unable to read data from the transport
connection: An existing connection was forcibly closed by the remote
host."
System.Net.Sockets.SocketException "An existing connection was
forcibly closed by the remote host"
Yes, I updated my service reference after these changes. Yes, I am targeting the .NET 4.5 framework. I don't think it's a firewall issue, as I'm using the same port (8000) in both cases, and I already have that port open.
What am I doing wrong?

Discovery endpoint not found, Timeout exception

I'm loading an host with a service called Service which implement the contract IService.
The binding is WSDualHttpBinding.
var host = new ServiceHost(typeof(Service));
host.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
host.AddServiceEndpoint(typeof(IService),new WSDualHttpBinding()
,endPointAddress);
In the client side i am trying to discover the endpoint.
EndpointAddress address = new EndpointAddress(new Uri(string.Format("http://{0}:{1}/Service/Client/Discovery", Environment.MachineName, "1111")));Environment.MachineName, 1111);
DiscoveryClient client =
new DiscoveryClient(new DiscoveryEndpoint(new WSDualHttpBinding(), address));
FindResponse find = client.Find(new FindCriteria(typeof(IService))
{ Duration = TimeSpan.FromSeconds(3) });
It is not working... I get a timeout exception after a minute without any reason.
i am running the host and the client on the same machine.
does anyone can detect what the problem is?
thanks

Categories