I have created a simple client-service setup for downloading applications from a server. The client creates a NetNamedPipeBinding and uses this to open a channel with the service to initiate the download process.
private void OpenServiceChannel()
{
InstanceContext instanceContext = new InstanceContext(this);
NetNamedPipeBinding binding = new NetNamedPipeBinding();
DuplexChannelFactory<ICloudProductService> duplexChannelFactory = new DuplexChannelFactory<ICloudProductService>(instanceContext, binding, new EndpointAddress(ADDRESS));
_proxy = duplexChannelFactory.CreateChannel();
}
The service then uses a Callback on the same channel to report progress to the client. This callback is defined as a part of the ServiceContract:
[ServiceContract(CallbackContract = typeof(IStatusProgressCallback))]
public interface ICloudProductService
{
[OperationContract]
[FaultContract(typeof(ServiceErrorCode))]
Task<IEnumerable<LauncherAppDetails>> GetAvailableApps(string tokenJson);
[OperationContract]
[FaultContract(typeof(ServiceErrorCode))]
Task Install(string tokenJson, LauncherAppDetails app);
}
With the event in said callback triggered by the Service when it needs to increment the download progress
public interface IStatusProgressCallback
{
[OperationContract]
void OnProgressUpdated(InstallProgress progress);
}
This all works beautifully on any half decent internet connection as the downloads are pretty fast. My issue is that on slow connections, the download is taking longer than the 1 minute default timeout for the binding. I could increase the timeout of this binding but I am concerned that this is not an ideal solution as it is bad practice due to possible security/performance issues.
Is there a way of configuring the binding on the client side so that the timeout period is refreshed on each successful callback (a keep-alive) such that as long as the server is passing incremental download progress, the connection will not time out.
Related
I'm creating an EventHub client application that sends message to an Event Hub on Azure.
Now when I have a message to send (every 5-6 seconds) I create a new instance of the class EventHubClient, then I use it to send data:
public async static void SendListOfMeasurements(string eventHubName, string connectionString, List<Measurement> measurementsList, int maxMessageSize)
{
// Create EventHubClient
EventHubClient client = EventHubClient.CreateFromConnectionString(
connectionString,
eventHubName);
...
Is it a good practice? Or is it better to create it only at startup and then use only the .Send method?
What is the best scenario in terms of performance? In the future the ammount of Sends could increase and also the quantity of messages
Creating a client directly using the EventHubClient.CreateFromConnectionString(...) will create a new tcp connection for each client.
However, when creating the client using a shared MessagingFactory
var factory = MessagingFactory.CreateFromConnectionString("your_connection_string");
var client = factory.CreateEventHubClient("MyEventHub");
the client will reuse the underlying tcp connection. From the docs: Create an Event Hubs client
No, It's necessary to create a new EventHubClient using the MessageFactory only the first time.
If I create a Client for every message I send, then a new connection is created and the old one remain opened (checked using netstat) resulting in a lot of "ESTABILISHED" connections.
Using a single client created for the first message, then recycling the client for the other messages, if the internet connection is lost then comes back, the tcp connection is automatically re-created.
Here is an example of a client creation using factory:
var endpointUri = ServiceBusEnvironment.CreateServiceUri("sb", eventHubNamespace, string.Empty);
MessagingFactory factory = MessagingFactory.Create(endpointUri, new MessagingFactorySettings
{
TokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(sasToken),
TransportType = TransportType.Amqp
});
factory.RetryPolicy = new RetryExponential(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(30), 3);
client = factory.CreateEventHubClient(eventHubName);
Yes its is correct, you could create a new EventHubClient each time, it would be fine.
Internally, it reuses the same underlying TCP connection if the connection string is the same. EventHubClient caches results of the MessageFactory.Create(...) methods it uses.
The following code works just fine in WinForms:
if (System.Configuration.ConfigurationManager.AppSettings["Data.UseNamedPipes"] == "true")
{
AES.Cloud.DataAccessLayer.TheEDGEContext.UseLocalDb = true;
string address = "net.pipe://localhost/" + Guid.NewGuid() + "/DataManager";
var svc = AES.InProcFactory.CreateInstance<AES.Cloud.DataService.EstimatingDataService, AES.Cloud.DataService.IDataManager>(2147483647, 2147483647, address);
DataService<AES.TheEdge.Framework.Services.Contracts.IDataManager>._namedPipeAddress = address;
var ss = svc.GetNextKey(); //<-- Calling service here.
}
but when I use WPF it times out (exception at the bottom but it's useless) when I call the service, however if I invoke this code on a different thread it works just fine in WPF:
System.Threading.ThreadPool.QueueUserWorkItem( new System.Threading.WaitCallback( (o) =>{
//same code as above... no problem, no timeout, no blocking
}
Is there some issue using the startup/main thread to do this sort of thing? Confused in Jupiter...
Exception:
System.TimeoutException: This request operation sent to net.pipe://localhost/c1a59720-11fc-49c3-9d82-8185203a6f5d/DataManager did not receive a reply within the configured timeout (00:01:00). The time allotted to this operation may have been a portion of a longer timeout. This may be because the service is still processing the operation or because the service was unable to send a reply message. Please consider increasing the operation timeout (by casting the channel/proxy to IContextChannel and setting the OperationTimeout property) and ensure that the service is able to connect to the client.
I'm writing a test application with signal r server and a web client and I wanted to know if there is a way to determine or have the server know which transport method the client is establishing with the server.
In regards to websockets which has a persistent two-way connection between the client and server or long polling which keeps polling the server until the server responds and then closes up the connection would there be any downside that I have to be aware of regarding the transport method not being web sockets outside of the persistent two-way connection especially if there are going to be many long running requests being made one after another?
I've noticed that making multiple requests from a client will be handled by the hub and returned when done, example I send a request to wait 10 seconds then a another request to wait 1 second. The Hub will respond to the 1 second wait request first then the 10 second delay, I am curious as to whether there is a thread per request created which is attached to the client via the same persistent duplex connection.
here is my example code.
class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
public class RunningHub : Hub
{
public void SendLongRunning(string name, string waitFor)
{
Clients.All.addMessage(name, "just requested a long running request I'll get back to you when im done");
LongRunning(waitFor);
Clients.All.addMessage(name, "I'm done with the long running request. which took " + waitFor + " ms");
}
private void LongRunning(string waitFor)
{
int waitTime = int.Parse(waitFor);
Thread.Sleep(waitTime);
}
}
JQuery Sample.
$(function () {
//Set the hubs URL for the connection
$.connection.hub.url = "http://localhost:9090/signalr";
// Declare a proxy to reference the hub.
var signalHub = $.connection.runningHub;
$('#url').append('<strong> Working With Port: ' + $.connection.hub.url + '</strong>');
// Create a function that the hub can call to broadcast messages.
signalHub.client.addMessage = function (name, message) {
//handles the response the message here
};
// Start the connection.
$.connection.hub.start().done(function () {
$('#sendlongrequest').click(function() {
signalHub.server.sendLongRunning($('#displayname').val(), $('#waitTime').val());
});
});
});
For ASP.NET Core;
var transportType = Context.Features.Get<IHttpTransportFeature>()?.TransportType;
Regarding the transport method:
You can inspect HubCallerContext.QueryString param transport:
public void SendLongRunning(string name, string waitFor)
{
var transport = Context.QueryString.First(p => p.Key == "transport").Value;
}
Regarding threading & long-running tasks:
Each request will be handled on a separate thread and the hub pipeline resolves the client-side promise when the hub method completes. This means that you can easily block your connection because of the connection limit in browsers (typically 6 connections at a time).
E.g.: if you use long-polling and you make six requests to the server, each triggering (or directly executing) a long-running operation, then you'll have six pending AJAX requests which only get resolved once the hub method is done, and you won't be able to make any further requests to the server until then. So you should use separate tasks for the long-running code and you should also not await those so the hub dispatcher can send its response without a delay.
If the client needs to know when the long-running task is done, then you should do a push notification from the server instead of relying on the .done() callback.
I have a piece of code that calls a WCF service that is hosted on a server.
The code keeps looping around and around calling this method over and over again. (It's asking for a 'status', so it's not doing any work at all).
That's fine except that after a short period of time I get an error:
This request operation sent to net.tcp://serverName:9001/service1 did not receive a reply within the configured timeout (00:00:09.9843754)
And suddenly i cannot get to the server at all EVER. I increased the timeout to 1min but still the same problem. Note that the program that hosts the service is doing nothing else, just offering it's 'status'. So it's not an issue with the WCF service app being busy.
I think it's a problem with the code calling the service because when i re-start the app it can connect to the service just fine ... until after another short time i get the timeout error again. For this reason i don't thnk it's a network error either, as when I restart the app it's ok for a period of time.
Here is the code i use to call the service. Do i need to dispose of the ChannelFactory after each call to clean it up or what am i doing worng?
NetTcpBinding binding = new NetTcpBinding(SecurityMode.Message);
binding.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
EndpointAddress endPoint = new EndpointAddress(new Uri(clientPath));
ChannelFactory<IClient> channel = new ChannelFactory<IClient>(binding, endPoint);
channel.Faulted += new EventHandler(channel_Faulted);
IClient client = channel.CreateChannel();
((IContextChannel)client).OperationTimeout = new TimeSpan(0, 0, 10);
ClientStatus clientStatus = client.GetStatus();
You do have to close client connections after you finish calling GetStatus. The best way to do this is to use a using block. But you can also do something like this after your call client.GetStatus()
ClientStatus clientStatus = client.GetStatus();
try
{
if (client.State != System.ServiceModel.CommunicationState.Faulted)
{
client.Close();
}
}
catch (Exception ex)
{
client.Abort();
}
I am trying to host a WCF service, using NetTcpBinding in a Windows service. (I'm going to use it as an API for various clients both Web and Windows32) Obviously, I am doing this within a test host before putting it in a Windows service.
I have the following contract:
namespace yyy.xxx.Server.API.WCF
{
[ServiceContract]
public interface ISecureSessionBroker
{
[OperationContract]
string GetSessionToken(string username, string encryptedPassword, string clientApiKey, string clientAddress);
}
}
with the following implementation:
namespace yyy.xxx.Server.API.WCF
{
public class SecureSessionBroker : ISecureSessionBroker
{
#region ~ from ISecureSessionBroker ~
public string GetSessionToken(string username, string encryptedPassword, string clientApiKey, string clientAddress)
{
return Guid.NewGuid().ToString();
}
#endregion
}
}
I am hosting the WCF service using the code below (within a class/method):
try
{
_secureSessionBrokerHost = new ServiceHost(typeof(SecureSessionBroker));
NetTcpBinding netTcpBinding = new NetTcpBinding();
_secureSessionBrokerHost.AddServiceEndpoint(typeof(ISecureSessionBroker), netTcpBinding, "net.tcp://localhost:8080/secureSessionBrokerTcp");
int newLimit = _secureSessionBrokerHost.IncrementManualFlowControlLimit(100);
// Open the ServiceHost to start listening for messages.
_secureSessionBrokerHost.Open();
}
catch (Exception ex)
{
throw;
}
The key thing here is that I do not want to have to rely on an App.config file. Everything must be configured programmatically. When I run this code, the service appears to come "up" and listen. (ie. I have no exceptions)
BUT when I use the client code below:
string secureSessionBrokerUrl = string.Format("{0}/secureSessionBrokerTcp","net.tcp://localhost/8080",url);
EndpointAddress endpointAddress=new EndpointAddress(secureSessionBrokerUrl);
System.ServiceModel.Channels.Binding binding = new NetTcpBinding();
yyy.xxx.Windows.AdminTool.API.WCF.SecureSessions.SecureSessionBrokerClient
client = new yyy.xxx.Windows.AdminTool.API.WCF.SecureSessions.SecureSessionBrokerClient(binding,endpointAddress);
string sessionToken=client.GetSessionToken("", "", ""); // exception here
MessageBox.Show(sessionToken);
... I always get an exception. At the moment, I am getting:
This request operation sent to
net.tcp://localhost:8080/secureSessionBrokerTcp
did not receive a reply within the
configured timeout (00:01:00). The
time allotted to this operation may
have been a portion of a longer
timeout. This may be because the
service is still processing the
operation or because the service was
unable to send a reply message.
Please consider increasing the
operation timeout (by casting the
channel/proxy to IContextChannel and
setting the OperationTimeout property)
and ensure that the service is able to
connect to the client.
So I guess it cannot resolve the service.
Where am I going wrong? How do I test for the existence of the service over TCP? I have used the SvcTraceViewer and I just get the same message, so no news there.
I would prefer to ask the user for a URL of the service, so they would enter "net.tcp://localhost:8080" or something, which would then be used as a BaseAddress for the various calls to the SecureSessionBroker (and other) WCF services ... without resorting to App.config.
Unfortunately, all the examples I can find all use the App.config.
Interestingly, I can host the service using the VS Host and the client connects fine. (Using:
D:\dev2008\xxx\yyy.xxx.Server>WcfSvcHost.exe /service:bin/debug/yyy.
xxx.Server.dll /config:App.config)
Ok, it came to me in a flash of inspiration.
I was using a Windows Form (alarm bells) to "host" the service. Clicking out of the form, I used a bit of code to call the service (included) on a button click. Of course, the service was not in its own thread, so the service could not respond.
I've fixed it by putting the Service container (which contains the host) within its own thread:
Thread thread = new Thread(new ThreadStart(_serviceWrapper.Start));
thread.Start();
The Start() method sets up the ServiceHost.
I incorrectly thought that while a WCF Service Host will create threads for incoming requests, it will only do this if it is in its own non-blocking thread (ie. not a UI thread).
Hope it helps someone else.