How to set network interface to use in WCF service? - c#

I dont have much knowledge on WCF. I have a WPF tablet application using WCF service to communicate with another instance of WPF application on other tablet.
WPF application places a pull request from other tablet's database via WCF service. This WCF service on the tablet fetches the data from database and sends it over TCP back to the requesting application. Same happens vice versa. Hence the synchronization is achieved. It happens over WiFi interface.
private const string WCFSERVICE_URL = "net.tcp://{0}:{1}/SynchronizeService"
Some code where we configure the client:
private DrillSynchronizeClient CreateConfigureClient(DrillNetInfo Drill)
{
DrillSynchronizeClient synchClient = new DrillSynchronizeClient();
string endpointUrl = String.Format(WCFSERVICE_URL, Drill.Ip, wcfServiceNetworkPort);
EndpointAddress serviceAddress = new EndpointAddress(endpointUrl);
NetTcpBinding netTcpBinding = new NetTcpBinding();
netTcpBinding.Security.Mode = SecurityMode.None;
netTcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
netTcpBinding.OpenTimeout = new TimeSpan(0, 5, 0);
netTcpBinding.CloseTimeout = new TimeSpan(0, 5, 0);
netTcpBinding.SendTimeout = new TimeSpan(0, 5, 0);
netTcpBinding.ReceiveTimeout = new TimeSpan(0, 5, 0);
netTcpBinding.MaxBufferPoolSize = int.MaxValue;
netTcpBinding.MaxBufferSize = int.MaxValue;
netTcpBinding.MaxReceivedMessageSize = int.MaxValue;
synchClient.Endpoint.Address = serviceAddress;
synchClient.Endpoint.Binding = netTcpBinding;
return synchClient;
}
This works fine when there is not internet connection available on these tablets.
When tablets are connected to internet via internet USB dongle, this synchronization stops working. Is the WCF trying to use the internet adapter(dongle) to resolve the WCF service IP address endpoint ?? Not sure, how to bind the WCF service to use only WLAN interface for communication.
Also please let me know where to specify/bind the source interface IP address to use when calling WCF request.
Thanks.

Related

WCF erratically timing out despite binding timeout settings

My system is composed of a bunch of WCF services that can be used by various clients.
The first thing I do is to run the services in interactive mode (I have a console popping and letting me know the service is started).
I can then run a given client that excercises any of my WCF services. When instantiated the client create a channel and associated timeouts in the following way:
var ctx = new InstanceContext(TestCallbackProxy.Instance);
string baseAddress = Constants.ServiceBaseAddress;
var binding = new NetNamedPipeBinding();
binding.MaxConnections = 10;
binding.OpenTimeout = System.TimeSpan.FromMinutes(1);
binding.CloseTimeout = System.TimeSpan.FromMinutes(1);
binding.ReceiveTimeout = System.TimeSpan.FromMinutes(5);
binding.SendTimeout = System.TimeSpan.FromMinutes(5);
var channelFactory = new DuplexChannelFactory<ITestService>(ctx, binding, new EndpointAddress(baseAddress + serviceName));
// Create channel to a specified endpoint
_channel = channelFactory.CreateChannel() as ITestService;
Now, the service eventually times out when left unused for a while, which from my understanding is expected. ie. The channel will simply vanish/be discarded by the system if unused - Something to do with reusablility and optimisation I believe.
However, when trying to prove this theory and reducing all the timeouts, I cannot get the service to break. ie. Should the service not timeout/break when trying to use it when left alone for more than 30sec? Used timeouts are:
binding.OpenTimeout = System.TimeSpan.FromMinutes(0.5);
binding.CloseTimeout = System.TimeSpan.FromMinutes(0.5);
binding.ReceiveTimeout = System.TimeSpan.FromMinutes(0.5);
binding.SendTimeout = System.TimeSpan.FromMinutes(0.5);
I think the reason is that you should be setting your timeouts on the service side, where you are setting up your hosts and defining endpoints, not on the client side. Here is an example:
var binding = new NetNamedPipeBinding();
binding.MaxConnections = 10;
binding.OpenTimeout = TimeSpan.FromMinutes(0.5);
binding.CloseTimeout = TimeSpan.FromMinutes(0.5);
binding.ReceiveTimeout = TimeSpan.FromMinutes(0.5);
binding.SendTimeout = TimeSpan.FromMinutes(0.5);
// Compose URIs
Uri uriBase = new Uri(baseAddress);
Uri uri = new Uri(baseAddress + something);
Uri uriMex = new Uri(baseAddress + something + "/mex");
// Create End Points
SomeHost = new CustomServiceHost(typeof(TestService), uriBase);
SomeHost.AddServiceEndpoint(typeof(ITestService), binding, uri);
SomeHost.AddServiceEndpoint(typeof(IMetadataExchange), binding, uriMex);
// Open the ServiceHost
SomeHost.Open();
Now you should see you service dying after 30sec.

Secured selfhosted WCF service: load certificate from file

I have a selfhosted WCF service publishing a REST web API. The service is configured programmatically, and currently is correctly working via HTTP.
Now I need to make it work over HTTPS, with an authentication based on certificate file.
I know the suggested way to do this is installing the certificate in the Windows Certificate Store on the server machine, but this way is not possible in my circumstances.
I need to load the certificate from the file.
After some resarches, I wrote my code, but when I try accessing the web service, a System.ServiceModel.CommunicationException is thrown, with the message:
An error occurred while making the HTTP request to ... 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.
Here's my code for the server side:
_host = new WebServiceHost(_hostedService, Uri);
//Configuring the webHttpBinding settings
var httpBinding = new WebHttpBinding();
httpBinding.Security.Mode = WebHttpSecurityMode.Transport;
httpBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
httpBinding.SendTimeout = new TimeSpan(0, 5, 0);
httpBinding.MaxBufferSize = 2147483647;
httpBinding.MaxBufferPoolSize = 2147483647;
httpBinding.MaxReceivedMessageSize = 2147483647;
httpBinding.ReaderQuotas.MaxDepth = 2147483647;
httpBinding.ReaderQuotas.MaxStringContentLength = 2147483647;
httpBinding.ReaderQuotas.MaxArrayLength = 2147483647;
httpBinding.ReaderQuotas.MaxBytesPerRead = 2147483647;
httpBinding.ReaderQuotas.MaxNameTableCharCount = 2147483647;
//Add the endpoint with the webHttpBinding settings to the WebServiceHost configuration
_host.AddServiceEndpoint(typeof(IService), httpBinding, Uri);
ServiceDebugBehavior stp = _host.Description.Behaviors.Find<ServiceDebugBehavior>();
stp.HttpHelpPageEnabled = false;
ServiceBehaviorAttribute sba = _host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
sba.InstanceContextMode = InstanceContextMode.Single;
ServiceMetadataBehavior smb = new ServiceMetadataBehavior() { HttpsGetEnabled = true };
_host.Description.Behaviors.Add(smb);
X509Certificate2 trustedCertificate = new X509Certificate2("certificate.pfx", "password");
_host.Credentials.ServiceCertificate.Certificate = trustedCertificate;
_host.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
Here's my code for the client side:
var httpBinding = new WebHttpBinding();
httpBinding.Security.Mode = WebHttpSecurityMode.Transport;
httpBinding.Security.Transport = new HttpTransportSecurity() { ClientCredentialType = HttpClientCredentialType.Certificate };
var httpUri = new Uri(String.Format("https://{0}:{1}", ipAddress, tcpPort));
var httpEndpoint = new EndpointAddress(httpUri);
var newFactory = new ChannelFactory<IService>(httpBinding, httpEndpoint);
newFactory.Endpoint.Behaviors.Add(new WebHttpBehavior());
X509Certificate2 trustedCertificate = new X509Certificate2("certificate.pfx", "password"); //SSL
newFactory.Credentials.ClientCertificate.Certificate = trustedCertificate;
var channel = newFactory.CreateChannel();
var response = channel.Ping("helo");
The Exception is thrown on the last line (channel.Ping("helo")).
I need to make it work WITHOUT installing the certificate on the server machine.
Thank you very much.
Bye.
As far as I know, when we host the self-hosted WCF service over Https, whatever way we use (load certificate file or configure the certificate via Windows Certificate Store), it is impossible to make the service works normally. The only way we need to do is binding the certificate manually by using the following command.
netsh http add sslcert ipport=0.0.0.0:8000 certhash=0000000000003ed9cd0c315bbb6dc1c08da5e6 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
Here is official document, wish it is useful to you.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate
https://learn.microsoft.com/en-us/windows/desktop/http/add-sslcert
Here are some examples were ever written by me.
WCF Service over HTTPS without IIS, with SSL Certificate from CERT and KEY strings or files
Chrome (v71) ERR_CONNECTION_RESET on Self Signed localhost on Windows 8 Embedded
Feel free to let know if there is anything I can help with.

External Clients unable to access WCF Communication Listener on Azure Service Fabric

I'm trying to migrate Azure Web Roles running WCF, to stateless services in Azure Service Fabric, using the WCF Communication Listener. Everything works in my local service cluster. After publishing to Azure, other services within the cluster are able to access the stateless WCF service, but external (internet) clients (including my dev machine) are unable to connect with a transient network error.
I verified that the load balancer in the resource group has rules/probes for ports 80 and 8080, and have tested with TCP and HTTP. I also tried to setup the partition resolver on the WCF client to point the "client connection endpoint" on the service cluster (from default, which works within the service cluster).
At this point, I'm not sure if I have a configuration issue, or if it's even possible for external (internet) clients to connect to stateless services running the WCF Communication Listener.
Here is my config:
WCF Communication Listener
private Func<StatelessServiceContext, ICommunicationListener> CreateListener()
{
return delegate (StatelessServiceContext context)
{
var host = new WcfCommunicationListener<IHello>(
wcfServiceObject: this,
serviceContext: context,
endpointResourceName: "ServiceEndpoint",
listenerBinding: CreateDefaultHttpBinding()
);
return host;
};
}
WCF Binding
public static Binding CreateDefaultHttpBinding()
{
var binding = new WSHttpBinding(SecurityMode.None)
{
CloseTimeout = new TimeSpan(00, 05, 00),
OpenTimeout = new TimeSpan(00, 05, 00),
ReceiveTimeout = new TimeSpan(00, 05, 00),
SendTimeout = new TimeSpan(00, 05, 00),
MaxReceivedMessageSize = int.MaxValue,
};
var quota = new XmlDictionaryReaderQuotas
{
MaxArrayLength = int.MaxValue,
MaxDepth = int.MaxValue
};
binding.ReaderQuotas = quota;
return binding;
}
ServiceManifest.xml (I've also used the default TCP binding with various ports)
<Endpoints>
<Endpoint Name="ServiceEndpoint" Protocol="http" Port="8080" />
</Endpoints>
WCF Console App
var address = new Uri("fabric:/ServiceFabricWcf.Azure/ServiceFabricWcf");
var client = GetClient(address, CreateDefaultHttpBinding());
try
{
var results = client.InvokeWithRetry(x => x.Channel.Hello());
System.WriteLine($"Results from WCF Service: '{results}'");
Console.ReadKey();
}
catch (Exception e)
{
System.Console.WriteLine("Exception calling WCF Service: '{e}'");
}
WCF Client
public static WcfServiceFabricCommunicationClient<IHello> GetClient(Uri address, Binding binding)
{
//ServicePartitionResolver.GetDefault(); Works with other services in cluster
var partitionResolver = new ServicePartitionResolver("<clientConnectionEndpointOfServiceCluster>:8080");
var wcfClientFactory = new WcfCommunicationClientFactory<IHello>(binding, null, partitionResolver);
var sfclient = new WcfServiceFabricCommunicationClient<IHello>(wcfClientFactory, address, ServicePartitionKey.Singleton);
return sfclient;
}
WCF Client Factory
public class WcfServiceFabricCommunicationClient<T> : ServicePartitionClient<WcfCommunicationClient<T>> where T : class
{
public WcfServiceFabricCommunicationClient(ICommunicationClientFactory<WcfCommunicationClient<T>> communicationClientFactory,
Uri serviceUri,
ServicePartitionKey partitionKey = null,
TargetReplicaSelector targetReplicaSelector = TargetReplicaSelector.Default,
string listenerName = null,
OperationRetrySettings retrySettings = null
)
: base(communicationClientFactory, serviceUri, partitionKey, targetReplicaSelector, listenerName, retrySettings)
{
}
}
Here's a way that works for a WCF Service with a WebHttpBinding: https://github.com/loekd/ServiceFabric.WcfCalc
Try changing your code so it doesn't use endpointResourceName but address containing an explicit URL instead. The URL should be the public name of your cluster, like mycluster.region.cloudapp.azure.com.
Edit: url should use node name, which is easier.
string host = context.NodeContext.IPAddressOrFQDN;
var endpointConfig = context.CodePackageActivationContext.GetEndpoint
("CalculatorEndpoint");
int port = endpointConfig.Port;
string scheme = endpointConfig.Protocol.ToString();
string uri = string.Format(CultureInfo.InvariantCulture,
"{0}://{1}:{2}/", scheme, host, port);
Here is my updated code, based on LoekD's answer.
Service Changes:
To make the service available to internet clients, you have to add an "Address" property to the WCFCommunicationListener to tell the service what endpoint to listen on (http://mycluster.region.azure.com or http://localhost)
Client Changes: Use the normal WCF client, without any of the WCFCommunicationListener references. Only use the WCFCommunicationListener client when inside of the service fabric (my original code works fine in this scenario).
WCF Server Listener
return delegate (StatelessServiceContext context)
{
string host = HostFromConfig(context);
if (string.IsNullOrWhiteSpace(host))
{
host = context.NodeContext.IPAddressOrFQDN;
}
var endpointConfig = context.CodePackageActivationContext.GetEndpoint("ServiceEndpoint");
int port = endpointConfig.Port;
string scheme = endpointConfig.Protocol.ToString();
//http://mycluster.region.cloudapp.azure.com or http://localhost
string uri = string.Format(CultureInfo.InvariantCulture, "{0}://{1}:{2}", scheme, host, port);
var listener = new WcfCommunicationListener<IHello>(
wcfServiceObject: this,
serviceContext: context,
listenerBinding: CreateDefaultHttpBinding(),
address: new EndpointAddress(uri)
);
return listener;
};
WCF Client Application
static void Main(string[] args)
{
System.Console.WriteLine("\nPress any key to start the wcf client.");
System.Console.ReadKey();
System.Console.WriteLine("*************Calling Hello Service*************");
try
{
var binding = CreateDefaultHttpBinding();
var address = new EndpointAddress("http://cluster.region.cloudapp.azure.com/"); //new EndpointAddress("http://localhost");
var results = WcfWebClient<IHello>.InvokeRestMethod(x => x.Hello(),binding, address );
System.Console.WriteLine($"*************Results from Hello Service: '{results}'*************");
Console.ReadKey();
}
catch (Exception e)
{
System.Console.WriteLine($"*************Exception calling Hello Service: '{e}'*************");
}
}
WCF Binding
public static Binding CreateDefaultHttpBinding()
{
var binding = new WSHttpBinding(SecurityMode.None)
{
CloseTimeout = new TimeSpan(00, 05, 00),
OpenTimeout = new TimeSpan(00, 05, 00),
ReceiveTimeout = new TimeSpan(00, 05, 00),
SendTimeout = new TimeSpan(00, 05, 00),
MaxReceivedMessageSize = int.MaxValue,
};
var quota = new XmlDictionaryReaderQuotas
{
MaxArrayLength = int.MaxValue,
MaxDepth = int.MaxValue
};
binding.ReaderQuotas = quota;
return binding;
}
Sample External/Internet WCF Client:
public abstract class WcfWebClient<T> where T : class
{
public static TResult InvokeRestMethod<TResult>(Func<T, TResult> method, Binding binding, EndpointAddress address)
{
var myChannelFactory = new ChannelFactory<T>(binding, address);
var wcfClient = myChannelFactory.CreateChannel();
try
{
var result = method(wcfClient);
((IClientChannel)wcfClient).Close();
return result;
}
catch (TimeoutException e)
{
Trace.TraceError("WCF Client Timeout Exception" + e.Message);
// Handle the timeout exception.
((IClientChannel)wcfClient).Abort();
throw;
}
catch (CommunicationException e)
{
Trace.TraceError("WCF Client Communication Exception" + e.Message);
// Handle the communication exception.
((IClientChannel)wcfClient).Abort();
throw;
}
}
}
You can also try to enable any address binding mode for the WCF service by putting the following attribute on the service implementation:
[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]

Calling a SAP ME web Service in wcf web service

I have a soap web service (sap me web service), I generated the wcf proxy. no problem, I developed a WinForms application no problem
Now i try to use it in my wcf service (webHttpBinding binding in domain network) but i have an authentication error:
WCF - The HTTP request is unauthorized with client authentication scheme 'Basic'. The authentication header received from the server was 'Basic realm="mySoapServiceName"
It is IIS User problem ?
Thanks
SAP is using basic auth. You need to specify the username and password after you have created the proxy, for example:
proxy.Credentials.UserName.UserName = "joe";
proxy.Credentials.UserName.Password = "doe";
BasicHttpBinding myBinding = new BasicHttpBinding();
myBinding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
EndpointAddress ea = new EndpointAddress("url de mon web service soap");
SfcServices.SFCProcessingInClient myClient = new SfcServices.SFCProcessingInClient(myBinding, ea);
myClient.ClientCredentials.UserName.UserName = _MESwsLogin;
myClient.ClientCredentials.UserName.Password = _MESwsPassword;
SfcServices.SFCStateRequestMessage_sync srm = new SfcServices.SFCStateRequestMessage_sync();
SfcServices.SFCStateRequest sr = new SfcServices.SFCStateRequest();
srm.SfcStateRequest = sr;
sr.SiteRef = new SfcServices.SiteRef();
sr.SiteRef.Site = _MESsite;
sr.SfcRef = new SfcServices.SFCRef();
sr.SfcRef.Sfc = "12345678903";
sr.includeSFCStepList = true;
SfcServices.SFCStateConfirmationMessage_sync response = myClient.FindStateByBasicData(srm);
strOrdreFab = response.SfcStateResponse.SFCStatus.Sfc.ShopOrderRef.ShopOrder;
strCodeProduit = response.SfcStateResponse.SFCStatus.Sfc.ItemRef.Item;
strIndice = response.SfcStateResponse.SFCStatus.Sfc.ItemRef.Revision;

How to get a web service object?

I'm working on implementing some code in C# using a web service, but my only reference is a Java code they used to load test.
Java gets the object calling by calling this
lotService=(LotService) ic.lookup("mes-webservices/lotService/remote");
where IC is an InitialContext object.
I need to do this same call on C# but I have no idea how. Is there a simple way just like this java method to do it in C#?
You can do similar thing in C# by adding service reference to web service. I assume your webservice and consuming client are both in .NET.
Psuedo code would be
LocationWebService objService = new LocationWebService(); // this is proxy class of web service created when you add web reference
string result = objService.GetLocationName(4); //call web method
Below are the steps:
Add service refrence in your project
Create ServiceClient instance
By using above created instance call methods it is exposing
That is it.
First right-click your project and select "Add Service Reference."
Once you have it you need to create the service client object. Whatever you named your service reference above you'll have a new type available in your project (named, I think, the service reference name appended with "Client" on the end. Example: if the service is FooService, you'll have a client type called FooServiceClient available.)
To instantiate, you need a binding. You can create it programmatically:
var binding = new BasicHttpBinding()
{
CloseTimeout = new TimeSpan(0, 1, 0),
OpenTimeout = new TimeSpan(0, 1, 0),
ReceiveTimeout = new TimeSpan(0, 10, 0),
SendTimeout = new TimeSpan(0, 1, 0),
AllowCookies = false,
BypassProxyOnLocal = false,
HostNameComparisonMode = HostNameComparisonMode.StrongWildcard,
MaxBufferSize = 65536,
MaxBufferPoolSize = 524288,
MaxReceivedMessageSize = 65536,
MessageEncoding = WSMessageEncoding.Text,
TextEncoding = Encoding.UTF8,
TransferMode = TransferMode.Buffered,
UseDefaultWebProxy = true
};
binding.ReaderQuotas.MaxDepth = 32;
binding.ReaderQuotas.MaxStringContentLength = 8192;
if (isHttps)
binding.Security = new BasicHttpSecurity() { Mode = BasicHttpSecurityMode.Transport };
Then you need an endpoint. Create like so:
var endpoint = new EndpointAddress(serviceUri);
Then just instantiate the service client:
var serviceClient = new FooServiceClient(binding, endpoint);
You can call your service methods from the service client instance.

Categories

Resources