I have a WCF Duplex Service having net.TCP Binding with which two client Applications, lets say Client A and Client B are connected. Client A gets data with respect to Time Tick from some source and notifies the Duplex Service by calling a method which in return passes that data to the Client B by calling back a method to the client B through a call back channel.
Here is the Contract Interface of my WCF Duplex Service:
namespace BroadcastorService
{
[ServiceContract(CallbackContract = typeof(IBroadcastorCallBack))]
public interface IBroadcastorService
{
[OperationContract(IsOneWay = true)]
void RegisterClients(string clientName);
[OperationContract(IsOneWay = true)]
void NotifyServer(EventDataType eventData);
}
public interface IBroadcastorCallBack
{
[OperationContract(IsOneWay = true)]
void BroadCastToClient(EventDataType eventData);
}
[DataContract]
public class EventDataType
{
[DataMember]
public string ClientName { get; set; }
[DataMember]
public string Data { get; set; }
}
}
}
Following is the code of the Service Class which inherits the contract interface:
namespace BroadcastorService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class BroadcastorService : IBroadcastorService
{
private static Dictionary<string, IBroadcastorCallBack> clients = new Dictionary<string, IBroadcastorCallBack>();
private static object locker = new object();
public void NotifyServer(EventDataType eventData)
{
lock(locker)
{
var InactiveClients = new List<string>();
foreach(var client in clients)
{
if(client.Key != eventData.ClientName)
{
try
{
if(client.Key == eventData.symbol)
{
client.Value.BroadCastToClient(eventData);
}
}
catch
{
InactiveClients.Add(client.Key);
}
}
}
if(InactiveClients.Count > 0)
{
foreach(var client in InactiveClients)
{
clients.Remove(client);
}
}
}
}
public void RegisterClients(string clientName)
{
if(clientName != null && clientName != "")
{
try
{
IBroadcastorCallBack callback = OperationContext.Current.GetCallbackChannel<IBroadcastorCallBack>();
lock(locker)
{
if(clients.Keys.Contains(clientName))
{
clients.Remove(clientName);
}
clients.Add(clientName, callback);
}
}
catch(Exception ex)
{
}
}
}
}
}
In the above code, Client B first establishes a callback channel with the Duplex Service by calling the RegisterClients(String ClientName) method. Client A calls NotifyServer(EventDataType eventData) method on each data tick which in return sends eventData to the client B through the callback channel.
Both of my client applications, Client A and Client B are developed on C# and the system is working perfectly fine. However, I would like to have a C++ application to replace Client A, i.e my C++ application is getting data with respect to time tick and I would like to pass the data to the client B by the same process as that of C# Client A application.
Is there any possibility of consuming a WCF service having netTCP binding in a C++ Application and calling NotifyServer(EventDataType eventData) method of the service through the WCF Service's Client object? If yes, then please share it with me.
Thank you!
Maybe you can create a managed C++ library which communicate with the C# library using WCF, and your native C++ library will use this managed lib as proxy.
A managed C++ library is the easy way to communicate a C++ library with C# using WCF.
Related
I have class (named Sender) that has a member of Icommunicator interface which defines a WCF Service Contract.
public class Sender
{
private ICommunicator channel;
private ChannelFactory<ICommunicator> factory = null;
Inside the same .cs file there's a class Receiver which inherits from Icommunicator.
The interface that defines the service contract is as follow:
[ServiceContract(Namespace = "P2PComm")]
public interface ICommunicator
{
[OperationContract(IsOneWay = true)]
void PostMessage(CommMessage msg);
// used only locally so not exposed as a service method
CommMessage GetMessage();
[OperationContract]
bool openFileForWrite(string fileName);
[OperationContract]
bool writeFileBlock(byte[] block);
[OperationContract]
void closeFile();
}
Inside the Sender class there's a connect function which uses the Icommunicator channel member directly to call the PostMessage function.
// attempts to connect to Receiver instance
// attempts a finite number of times to connect to a Receiver
// first attempt to send will throw exception of no listener
// at the specified endpoint
// to test that we attempts to send a connect message
public bool connect(string toAddress)
{
int timeToSleep = 500;
CreateSendChannel(toAddress);
CommMessage connectMsg = new CommMessage(CommMessage.MessageType.connect);
connectMsg.to = toAddress;
connectMsg.from = fromAddress;
connectMsg.command = Msg.Command.connect;
while (true)
{
try
{
channel.PostMessage(connectMsg);
tryCount = 0;
lastUrl = toAddress;
return true;
}
catch (Exception ex)
{
if (++tryCount < maxCount)
{
Thread.Sleep(timeToSleep);
}
else
{
lastError = ex.Message;
lastUrl = "";
tryCount = 0;
return false;
}
}
}
}
I don't understand what is happening here I'm not inheriting from the Icommunicator interface but I'm using it to call one of its function, so which function gets called in this case ?
When I ran it with a debugger it did not step into a function, yet it was still able to send a message.
Currently, I have 2 solutions in Visual Studio 2017:
A Windows forms application
A Windows service application hosting a WCF service class library (.dll)
And I need to communicate between them, in a cyclic way, as followed by the image below. The numbers represent the order.
The problem is, I'm actually able to communicate between WF and WCF application by using the request-replay operation contract (represented by numbers 1 and 4). But I'm not sure how to acomplhish steps 2 and 3.
Code for WCF interface:
namespace SmithWcfService {
[ServiceContract]
public interface ISmithWcfService {
[OperationContract]
void SendRequest( ); //Operation called by Windows Forms
}
}
Code for WCF interface implementation
namespace SmithWcfService {
public class SmithWcfService : ISmithWcfService {
public void SendRequest( ) {
//Ok, now I need to call Windows service application
}
}
}
Code for Windows service
namespace SmithWindowsService {
static class Program {
static void Main( ) {
ServiceBase[ ] ServicesToRun;
ServicesToRun = new ServiceBase[ ] {
new SmithWindowsService( )
};
ServiceBase.Run( ServicesToRun );
}
}
}
namespace SmithWindowsService {
public partial class SmithWindowsService : ServiceBase {
private ServiceHost host;
public SmithWindowsService( ) {
InitializeComponent( );
}
protected override void OnStart( string[ ] args ) {
host = new ServiceHost( typeof( SmithWcfService.SmithWcfService ) );
host.Open( );
}
}
}
If the windows service hosts your WCF service, you can simply pass anything it needs (callbacks, values, settings) at service start. You could pass a method of the windows service as Func<Input2, Output3> that the WCF service should call.
Without your code it's hard to tell where you need to put it though. Normally, it goes into your custom ServiceHostFactory.
Example of service with callback:
namespace SmithWcfService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class SmithWcfService : ISmithWcfService
{
private Func<string, int> callback;
public SmithWcfService(Func<string, int> callback)
{
this.callback = callback;
}
public void SendRequest()
{
//Ok, now I need to call Windows service application:
var output = this.callback("input");
}
}
}
Example of hosting:
namespace SmithWindowsService
{
public partial class SmithWindowsService : ServiceBase
{
private ServiceHost host;
public SmithWindowsService( )
{
InitializeComponent( );
}
protected override void OnStart(string[] args)
{
var instance = new SmithWcfService.SmithWcfService(this.SomeMethodYouWantToCallIn);
host = new ServiceHost(instance, new Uri("your.url.com"));
host.Open( );
}
private int SomeMethodYouWantToCall(string data)
{
// do things...
}
}
}
We have a singleton service fabric service that needs to communicate to a partitioned service, both of which are running in a secure cluster with certificate-based authentication. We are using ServicePartitionClient to do the talking. What follows is a simplified version of our implementations of ICommunicationClient and ICommunicationClientFactory as required by the ServicePartitionClient.
The client:
public class HttpCommunicationClient : ICommunicationClient
{
// Lots of fields omitted for simplicity.
public HttpCommunicationClient(HttpClientWrapper client, Uri baseAddress)
{
this.HttpClient = client;
this.BaseAddress = baseAddress;
}
public Uri BaseAddress { get; set; }
// Wraps System.Net.Http.HttpClient to do stuff like add default headers
public HttpClientWrapper HttpClient { get; }
public ResolvedServicePartition ResolvedServicePartition { get; set; }
public string ListenerName { get; set; }
public ResolvedServiceEndpoint Endpoint { get; set; }
public Uri GetUri(string relativeUri)
{
return new Uri(this.BaseAddress, relativeUri);
}
}
The factory:
// Note that this base class is under the
// Microsoft.ServiceFabric.Services.Communication.Client namespace,
// it's not something we wrote
public class HttpCommunicationClientFactory :
CommunicationClientFactoryBase<HttpCommunicationClient>
{
// Lots of fields omitted for simplicity.
private readonly HttpClientWrapper client;
public HttpCommunicationClientFactory(
ServicePartitionResolver resolver,
IEnumerable<IExceptionHandler> exceptionHandlers)
: base(resolver, exceptionHandlers, null)
{
// There's a bunch of other args that are omitted for clarity.
this.client = new HttpClientWrapper();
}
protected override Task<HttpCommunicationClient> CreateClientAsync(
string endpoint,
CancellationToken cancellationToken)
{
HttpCommunicationClient client = new HttpCommunicationClient(
this.client,
new Uri(endpoint));
if (this.ValidateClient(endpoint, client))
{
return Task.FromResult(client);
}
else
{
throw new ArgumentException();
}
}
protected override bool ValidateClient(HttpCommunicationClient client)
{
// Not much to validate on httpclient
return true;
}
protected override bool ValidateClient(
string endpoint,
HttpCommunicationClient client)
{
return !string.IsNullOrWhiteSpace(endpoint);
}
protected override void AbortClient(HttpCommunicationClient client)
{
}
}
And here's an example of how everything is being constructed and used:
internal class FooFabricClient : IDisposable
{
// Lots of fields omitted for simplicity.
private readonly HttpCommunicationClientFactory clientFactory;
private readonly ServicePartitionClient<HttpCommunicationClient> partitionClient;
public FooFabricClient(Uri fabricUri, ServicePartitionKey partitionKey = null)
{
this.clientFactory = new HttpCommunicationClientFactory(
ServicePartitionResolver.GetDefault());
this.partitionClient = new ServicePartitionClient<HttpCommunicationClient>(
this.clientFactory,
fabricUri,
partitionKey,
retrySettings: new OperationRetrySettings());
}
public async Task<Foo> GetFooAsync()
{
return await this.partitionClient.InvokeWithRetryAsync(async (client) =>
{
// Note: See above for actual GetUri() implementation and context,
// but this will give us a Uri composed of the client's BaseAddress
// (passed in during HttpCommunicationClientFactory.CreateClientAsync())
// followed by "/foo"
Uri requestUri = client.GetUri("foo");
return await client.HttpClient.GetAsync<Foo>(requestUri);
});
}
Now, the issue is that when I call GetFooAsync(), it throws an exception saying this:
Could not establish trust relationship for the SSL/TLS secure channel. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
After some debugging, we found that this is most likely due to the fact that we get the internal service fabric IP address (e.g., 10.0.0.4) as HttpCommunicationClient.BaseAddress, so when we make our API call the server cert doesn't validate against the domain in the request. As a temporary fix we've done the following in the calling service:
ServicePointManager.ServerCertificateValidationCallback += (a, b, c, d) => true;
Of course, we'd rather not just blindly say "yup, looks good" when validating server certs, so how should we go about resolving this issue? Is there another way to set up the client(s) so that they can properly validate the server cert on the request without needing our own callback? Or do we just need to put acceptable thumbprints in our config and compare against those in the ServicePointManager callback or whatever?
I am trying to pass a custom object from self hosted signalr hub server to all the clients, the method in client side not getting invoked .But if the same custom class object is passed from client to server works fine, meaning it invokes the server method.
below is the sample code :
public class ChatHub : Hub
{
public void Send(DataContract message)
{
//below call not reaching to client while passing custom obj
Clients.All.SendMessage(message);
//below string passing works - means invokes client method
Clients.All.SendMsg("test");
}
}
custom class defined in both client and server project via dll:
public class DataContract
{
public string Name
{
get;set;
}
public int Id
{
get;set;
}
}
client side method:
public class SignalRClient
{
HubConnection hubConnection = null;
IHubProxy chat;
public SignalRClient()
{
hubConnection = new HubConnection("https://localhost/");
chat = hubConnection.CreateHubProxy("ChatHub");
}
public void StartConnection()
{
if (hubConnection != null)
{
hubConnection.Start().Wait();
}
chat.On<DataContract>("SendMessage", (stock) =>
{
Console.WriteLine("name {0} id {1}", stock.Name, stock.Id.ToString());
});
chat.On<string>("SendMsg", (message) =>
{
Console.WriteLine(message);
});
}
public void SendMessage(DataContract dd)
{
dd.Name = "test";
chat.Invoke("Send", dd).Wait();
}
public void SendMessage(string msg)
{
chat.Invoke("SendMsg", "Console app", msg).Wait();
}
}
//program.cs
main()
{
SignalRClient client = new SignalRClient();
client.StartConnection();
string msg = null;
while ((msg = Console.ReadLine()) != null)
{
DataContract dd = new DataContract { Name = "arun", Id = 9 };
//below calls reaches to server both string type and custome obj
client.SendMessage(dd);
client.SendMessage("client");
}
}
Any clue on why when calling from server (i.e Clients.All.SendMessage(message); ) not invoking client method when param is custom object.
Thanks in advance.
I'm playing around with hooking up an in-game console to a WCF interface, so an external application can send console commands and receive console output. To accomplish this I created the following service contracts:
public interface IConsoleNetworkCallbacks
{
[OperationContract(IsOneWay = true)]
void NewOutput(IEnumerable<string> text, string category);
}
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IConsoleNetworkCallbacks))]
public interface IConsoleInterface
{
[OperationContract]
void ProcessInput(string input);
[OperationContract]
void ChangeCategory(string category);
}
On the server I implemented it with:
public class ConsoleNetworkInterface : IConsoleInterface, IDisposable
{
public ConsoleNetworkInterface()
{
ConsoleManager.Instance.RegisterOutputUpdateHandler(OutputHandler);
}
public void Dispose()
{
ConsoleManager.Instance.UnregisterOutputHandler(OutputHandler);
}
public void ProcessInput(string input)
{
ConsoleManager.Instance.ProcessInput(input);
}
public void ChangeCategory(string category)
{
ConsoleManager.Instance.UnregisterOutputHandler(OutputHandler);
ConsoleManager.Instance.RegisterOutputUpdateHandler(OutputHandler, category);
}
protected void OutputHandler(IEnumerable<string> text, string category)
{
var callbacks = OperationContext.Current.GetCallbackChannel<IConsoleNetworkCallbacks>();
callbacks.NewOutput(text, category);
}
}
On the client I implemented the callback with:
public class Callbacks : IConsoleNetworkCallbacks
{
public void NewOutput(IEnumerable<string> text, string category)
{
MessageBox.Show(string.Format("{0} lines received for '{1}' category", text.Count(), category));
}
}
Finally, I establish the service host with the following class:
public class ConsoleServiceHost : IDisposable
{
protected ServiceHost _host;
public ConsoleServiceHost()
{
_host = new ServiceHost(typeof(ConsoleNetworkInterface), new Uri[] { new Uri("net.pipe://localhost") });
_host.AddServiceEndpoint(typeof(IConsoleInterface), new NetNamedPipeBinding(), "FrbConsolePipe");
_host.Open();
}
public void Dispose()
{
_host.Close();
}
}
and use the following code on my client to establish the connection:
protected Callbacks _callbacks;
protected IConsoleInterface _proxy;
protected void ConnectToConsoleServer()
{
_callbacks = new Callbacks();
var factory = new DuplexChannelFactory<IConsoleInterface>(_callbacks,
new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/FrbConsolePipe"));
_proxy = factory.CreateChannel();
_proxy.ProcessInput("Connected");
}
So what happens is that my ConnectToConsoleServer() is called and then it gets all the way to _proxy.ProcessInput("Connected");. In my game (on the server) I immediately see the output caused by the ProcessInput call, but the client is still stalled on the _proxy.ProcessInput() call.
After a minute my client gets a JIT TimeoutException however at the same time my MessageBox message appears.
So obviously not only is my command being sent immediately, my callback is being correctly called. So why am I getting a timeout exception?
Note: Even removing the MessageBox call, I still have this issue, so it's not an issue of the GUI blocking the callback response.
You need to specify CallbackBehavior for your client class implementing the Callback interface
[CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Multiple, UseSynchronizationContext=false)]
See the following for a better explanation
http://www.switchonthecode.com/tutorials/wcf-callbacks-hanging-wpf-applications
It's possible that it's your _proxy.ProcessInput("Connected") which is blocking the call - this is consistent with your timeout experience, as the server sends the response immediately, but the client can't receive it as it's stuck on "ProcessInput". When your call finally times out, the blocking call terminates, at which point the callback completes.
To verify this, could you try invoking using this (non blocking) call instead?
((Action)(() => _proxy.ProcessInput("Connected"))).BeginInvoke(null, null);