I have a WCF Service and WCF Client working on Duplex Channel using netTCPBinding.
I store connected users in a dictionary ( Dictionary<int userID,CallbackInstance instance> )
When user Disconnect regularly, call Disconnect from service and I remove the user from my connected user list. It works fine.
but when client pc disconnects unregularly, client could not call Disconnect method, so client still in connected user list, that's the problem. Because when my WCF server check server for online users for a callback, server try to call client's callback method, but client is not available, and my WCF Server App crash.
Is it possible to check client status before calling callback instance?
Make sure all the properties timeouts are set to automatically remove inactive clients then catch the timeout exception in a try catch block and remove it from your dictionary.
I fix this with:
1.Method to ping from client to server to keep the conection active for each 30 seconds.
2.On server binding, ReceiveTimeout with 1 minute.
3.Foreach callback created a IcommunicationObject, using the Closed event to remove the inactive client.
//Adding a client callback
OperationContext context = OperationContext.Current;
ICallback callback = context.GetCallbackChannel();
ICommunicationObject obj = (ICommunicationObject)callback;
obj.Closed += new EventHandler(obj_Closed);
//Event for inactive clients
void obj_Closed(object sender, EventArgs e)
{
if (_callbacks.ContainsValue(((ITecnobelRemoteServiceCallback)sender)))
{
var item = _callbacks.First(kvp => kvp.Value == ((ITecnobelRemoteServiceCallback)sender));
_callbacks.Remove(item.Key);
treeViewClients.Nodes.RemoveByKey(item.Key.Id);
treeViewClients.Refresh();
_registeredUsers--;
listBoxStatus.Items.Add(String.Format("Usuário {0} estava inativo e foi removido", item.Key.Id));
}
}
Related
I'm working on a .NET web app using SignalR with the hub class similar to the example class below:
public class ContosoChatHub : Hub
{
public override Task OnConnected()
{
// Add your own code here.
// For example: in a chat application, record the association between
// the current connection ID and user name, and mark the user as online.
// After the code in this method completes, the client is informed that
// the connection is established; for example, in a JavaScript client,
// the start().done callback is executed.
return base.OnConnected();
}
public override Task OnDisconnected()
{
// Add your own code here.
// For example: in a chat application, mark the user as offline,
// delete the association between the current connection id and user name.
return base.OnDisconnected();
}
public override Task OnReconnected()
{
// Add your own code here.
// For example: in a chat application, you might have marked the
// user as offline after a period of inactivity; in that case
// mark the user as online again.
return base.OnReconnected();
}
}
More specific, my web app serves as hub for connecting tablets. when i close the app on the tablet it does not trigger instantly the OnDisconnected task, taking up to 20 seconds or more (server tries to reconnect with the client).
My question is, which method should I use in order to detect the connection loss as soon as it happens or, is there a connection state handler that triggers when the connection is lost?
In order to prevent the data loss (considering a tablet online when in fact it's not) I really need to handle the disconnecting event.
Any help is much appreciated!
Later edit:
I've also included the following lines in the Global.asax file
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(6);
GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(2);
in the Application Start method. The values seem to be saved, as seen in debug and actually reduce the time by half, from 20-30 to 12-14 seconds, but it's still not as close to 2-3 seconds.
You can detect the server disconnection from SignalR client:
$.connection.hub.disconnected(function () {
alert('Server has disconnected');
});
This is the official documentation when each method called:
When OnConnected, OnDisconnected, and OnReconnected are called
Each time a browser navigates to a new page, a new connection has to
be established, which means SignalR will execute the OnDisconnected
method followed by the OnConnected method. SignalR always creates a
new connection ID when a new connection is established.
The OnReconnected method is called when there has been a temporary
break in connectivity that SignalR can automatically recover from,
such as when a cable is temporarily disconnected and reconnected
before the connection times out. The OnDisconnected method is called
when the client is disconnected and SignalR can't automatically
reconnect, such as when a browser navigates to a new page. Therefore,
a possible sequence of events for a given client is OnConnected,
OnReconnected, OnDisconnected; or OnConnected, OnDisconnected. You
won't see the sequence OnConnected, OnDisconnected, OnReconnected for
a given connection.
The OnDisconnected method doesn't get called in some scenarios, such
as when a server goes down or the App Domain gets recycled. When
another server comes on line or the App Domain completes its recycle,
some clients may be able to reconnect and fire the OnReconnected
event.
I'm trying to build multiple client - one server structure. Each client makes connection to server and server maintains those connection.
Client have two thread(thread1, thread2) running asynchronously and sharing 1 socket to send, receive. Two thread each contain multiple send, receive function. For server to serve each client, it also have to make two client handler that share 1 socket.
I wanna have my server to create client handler routine(asynchronous task) only when their(each client) socket is ready-to-read to save resource. I'm trying to implement this feature using 'Socket.Select' method.
Here's my assumed implementation of server side select routine using code close to c#.
server.selectRoutine(){
while(!serverSocket.closed())
{
checkReadList client_connect_socket_list_copy=new ArrayList<Socket>(client_connect_socket_list) ;
Socket.Select(client_connect_socket_list_copy, null,null) ;
foreach(Socket s in client_connect_socket_list_copy)
{
client_connect_info info=client_connect_info_dict.Items[s] ;
if(!info.is_client_thread1_handler_active)
{
info.is_client_thread1_active=true ;
clServerEntranceHandler handler=new clServerEntranceHandler() ;
Task.Run(handler.run()) ;
}
if(!info.is_client_thread2_handler_active)
{
info.is_client_thread2_active=true ;
clServerExitHandler handler=new clServerExitHandler() ;
Task.Run(handler.run()) ;
}
}
Code Explanation
Client_connect_socket_list: contains currently connected clients
client_connect_info_dict: client connect info dictionary mapped for each socket as key.
client_connect_info: consist of (is_client_thread1_Handler_Active, is_client_thread2_Handler_Active)
Select only those sockets that are ready to be read after Socket.Select
Run handler for those sockets where handler is currently not made, info.is_client_thread1_Handler_Active=false. And set info.is_client_thread1_Handler_Active=true. This is because there will be multiple recv function inside already-made handler and they are in blocking mode. So when client sends message to server destined to recv function blocked in already-made handler, Socket.Select will also think this socket as ready-to-read and it will end up creating new handler. To avoid this, i need to check whether handler is active. After handler finishes, it will set info.is_client_thread1_Handler_Active= false inside. Same for client_thread2 handler.
I think i now have to synchronize this routine with each handler already made. For example, avoid situation like recv function inside handler P snatch message A inside message queue of handler P's socket after Socket.Select saw message A inside message queue. Then handler P will end and set info.is_Handler_Active=false. Then selectRoutine will create new handler again for message A already read by handler P and there goes trouble.
I wanna know whether there is serious problem with this scheme that i should not use it. And if there is, then i wanna know better ways to implement multiple client - one server with room for scaling.
I host the signalR client in IIS as a web site.
And I tested send message from server to this client, it works ok.
Then I shutdown the client site, and restart it after 10 mins.
Again, I send a message to this client, it still works.
My question is why the connection state didn't change?
BTW, I have state change event callback method
connection.StateChanged += (x) => {
OnStateChange(connectionId, x);
};
private void OnStateChange(string connectionId, StateChange stateChange)
{
StringBuilder text = new StringBuilder();
text.Append("ConnectiId:").Append(connectionId).Append(Environment.NewLine);
text.Append("OldState:").Append(stateChange.OldState.ToString()).Append(Environment.NewLine);
text.Append("NewState:").Append(stateChange.NewState.ToString()).Append(Environment.NewLine);
if(stateChange.NewState == ConnectionState.Disconnected)
{
//reconnect
}
Util.Log("signalR_statechange", text.ToString(), false);
}
There are no logs when I shutdown or restart the site.
go to this thread read...the below Continuation ...also there is no need onstatechange event... read msdn link it will more help for you
How to handle connection lifetime events in the Hub class
Typical reasons for handling connection lifetime events are to keep track of whether a user is connected or not, and to keep track of the association between user names and connection IDs. To run your own code when clients connect or disconnect, override the OnConnected, OnDisconnected, and OnReconnected virtual methods of the Hub class, as shown in the following example.
public class ContosoChatHub : Hub
{
public override Task OnConnected()
{
// Add your own code here.
// For example: in a chat application, record the association between
// the current connection ID and user name, and mark the user as online.
// After the code in this method completes, the client is informed that
// the connection is established; for example, in a JavaScript client,
// the start().done callback is executed.
return base.OnConnected();
}
public override Task OnDisconnected()
{
// Add your own code here.
// For example: in a chat application, mark the user as offline,
// delete the association between the current connection id and user name.
return base.OnDisconnected();
}
public override Task OnReconnected()
{
// Add your own code here.
// For example: in a chat application, you might have marked the
// user as offline after a period of inactivity; in that case
// mark the user as online again.
return base.OnReconnected();
}
}
When OnConnected, OnDisconnected, and OnReconnected are called
Each time a browser navigates to a new page, a new connection has to be established, which means SignalR will execute the OnDisconnected method followed by the OnConnected method. SignalR always creates a new connection ID when a new connection is established.
The OnReconnected method is called when there has been a temporary break in connectivity that SignalR can automatically recover from, such as when a cable is temporarily disconnected and reconnected before the connection times out. The OnDisconnected method is called when the client is disconnected and SignalR can't automatically reconnect, such as when a browser navigates to a new page. Therefore, a possible sequence of events for a given client is OnConnected, OnReconnected, OnDisconnected; or OnConnected, OnDisconnected. You won't see the sequence OnConnected, OnDisconnected, OnReconnected for a given connection.
The OnDisconnected method doesn't get called in some scenarios, such as when a server goes down or the App Domain gets recycled. When another server comes on line or the App Domain completes its recycle, some clients may be able to reconnect and fire the OnReconnected event.
For more information, see Understanding and Handling Connection Lifetime Events in SignalR.
Caller state not populated
The connection lifetime event handler methods are called from the server, which means that any state that you put in the state object on the client will not be populated in the Caller property on the server. For information about the state object and the Caller property, see How to pass state between clients and the Hub class later in this topic.
How to get information about the client from the Context property
To get information about the client, use the Context property of the Hub class. The Context property returns a HubCallerContext object which provides access to the following information:
The connection ID of the calling client.
string connectionID = Context.ConnectionId;
The connection ID is a GUID that is assigned by SignalR (you can't specify the value in your own code). There is one connection ID for each connection, and the same connection ID is used by all Hubs if you have multiple Hubs in your application.
the reason is:
did you close the client browser after shut down the iis?
I can tell you that the client is still connecting even if you shut down the iis.
because the signalr client html file not necessary hosted in iis, it can connect even it is hosted in the client side.
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();
}