We're building a WCF server (.NET 4.0). It will only use net.tcp transport.
When a client closes the TCP connection, the server gets unhandled CommunicationException, and terminates.
Q1. How do I handle the CommunicationException so the server does not terminate and continues serving other clients?
Q2. In the handler, how do I get SessionId of the session that was aborted? I need this to do clean up some session-specific data.
Thanks in advance!
P.S. The connection is over the Internet, so the socket may be closed anytime, regardless on whether the client disconnects gracefully, or not.
Any WCF channel implements ICommunicationObject , which provides events for the channel lifetime.
You should listen to the Faulted event
The sessionId can be accessed as always from the OperationContext.Current property.
When your client open the channel (on the first operation), register to the adequate events:
OperationContext.Current.Channel.Faulted += new EventHandler(Channel_Faulted);
OperationContext.Current.Channel.Closed += new EventHandler(Channel_Faulted);
and:
void Channel_Faulted(object sender, EventArgs e)
{
Logout((IContextChannel)sender);
}
protected void Logout(IContextChannel channel)
{
string sessionId = null;
if (channel != null)
{
sessionId = channel.SessionId;
}
[...]
}
ICommunicationObject obj = (ICommunicationObject)callback;
obj.Closed += new EventHandler((a, b) =>
{
if (list.Exists(cli => cli.CallbackService == (ITecnobelRemoteServiceCallback)a))
{
var query = (from cc in list where cc.CallbackService == (ITecnobelRemoteServiceCallback)a select cc).ToList();
query.ForEach(
delegate (Client ccc)
{
ccc.CallbackService = null;
});
}
});
Related
I am building a server selection screen and have a bunch of IP addresses that need to be pinged in order to know the remote hosts are reachable and to establish a TCP connection afterwards to request their data (like connected playerCount and MOTD, think of Minecraft Multiplayer Selection screen, you don't connect to the game, only requesting data from the endpoint).
The problem is that Ping is always unsuccessful, because the PingReply is null.
For generic use cases, I have wrapped the call with an event that gets fired when the Ping Reply is received back to the sender.
public static async void PingNET(string address, int timeOutMs, System.Action<PingReply> onCompleted)
{
await Task.Run(() => { //to continue unity main thread, start new thread
AutoResetEvent waiter = new AutoResetEvent(false);
var pingSender = new System.Net.NetworkInformation.Ping ();
// call handler when PingCompleted event is raised
pingSender.PingCompleted += (sender, e) =>
{
Debug.LogError ("Error and reply is always null! See: Error is null?: "
+ (e.Error == null) + " - Reply is null?: " + (e.Reply == null));
// If the operation was canceled, display a message to the user.
if (e.Cancelled) {
Debug.LogWarning("Ping canceled.");
}
// If an error occurred, display the exception to the user.
if (e.Error != null) {
Debug.LogError ("Ping failed:" + e.Error.ToString ());
}
if(onCompleted != null)
onCompleted(e.Reply);
};
// Send the ping asynchronously (uses internal new thread,
// so program does not freeze until reply received)
pingSender.SendAsync(address, timeOutMs, waiter);
});
}
Unity Test Runner Unit Test:
[Test]
public void PingTest(){
List<string> addresses = new List<string>(){
"127.0.0.1", "localhost", "www.stackoverflow.com", "www.google.com"
};
for (int i = 0; i < addresses.Count; i++)
{
Ping(addresses[i]);
}
}
private void Ping(string address){
NetworkUtils.PingNET(address, 10000, new System.Action<PingReply>((PingReply reply) => {
if (reply != null && reply.Status == IPStatus.Success)
{
Debug.Log($"Address: {reply.Address.ToString()}");
Debug.Log($"RoundTrip time: {reply.RoundtripTime}");
}
else //this gets called obviously
{
Debug.LogError("PING REPLY ERROR " + reply?.Status);
}
}));
}
Side Info: (Not directly tied to the question)
The reason why I use Ping in the first place is to prevent freeze (deadlock) on the client when the server goes offline while the connection is still tied to it and requesting data to the now unavailable remote host endpoint of the server. I could notify all connected clients that the server is shutting down, but this of course will not work when the server immediately lost connection to the internet.
So all in all, when the ping fails, it should handle in the UI logic to display the server is unreachable. Otherwise if it was successful, a TCP connection to the server is created to send a request. The server that listens on the same port sends a response with the data (playerCount, MOTD, etc.). After that, the client deseralizes the byte array to the datatypes and fires an event, which is subscribed by the UI to gather the deserealized data and display those.
The sending/receiving part with UI already works, just the ping not.
Is this process overcomplicated? Let me know if it could be simplified.
Any help is greatly appreciated.
So I have a project where I need to create failover between two FIX quote hosts in case of failure.
The FixApplication (IApplication) OnLogout() is nice to hook an event to when a socket is dropped or you are logged out. This is simple. But this only works if the socket connection was successful to start off with.
If you start up the application and the host is down, no method is actually called for this anywhere. Not in IApplication, nor in the socket IInitiator. No error is thrown anywhere either, the QuickFix initiator will simply just silently retry.
I am using a Timer with a callback right now to manually poll every so often and check if Initiator IsLoggedOn() is false. Then triggering a failover.
But this is really clunky and not elegant.
Is there any other event or method I can hook into to receive the notice for socket network failures BEFORE a network connection and session is established successfully?
Thanks!
Timer t = new Timer(5000)
t.Elapsed += CheckSocketConnected;
private void CheckSocketConnected(object source, ElapsedEventArgs e)
{
var connected = socketInitiator.IsLoggedOn;
if (!connected)
{
SwitchToAlternateProvider();
}
}
Well, after realising the limitation of the QuickFix/N component, I would never receive any feedback from this library if the host was down.
My resolution was to just simply use the following piece of code to check if socket was open before starting the connection in QuickFix/n
bool IsPortOpen(string host, int port, TimeSpan timeout)
{
try
{
using(var client = new TcpClient())
{
var result = client.BeginConnect(host, port, null, null);
var success = result.AsyncWaitHandle.WaitOne(timeout);
if (!success)
{
return false;
}
client.EndConnect(result);
}
}
catch
{
return false;
}
return true;
}
I have a server and many clients. Server needs to know when client disconnects ungracefully (doesn't send TCP FIN), so that it doesn't have hanging connection and other disposable objects associated with this client.
Anyway, I read this and decided to go with adding a "keepalive message to the application protocol" (contains only header bytes) and "explicit timer assuming the worst" methods from the linked blog.
When client connects (btw I am using TcpListener and TcpClient), server starts a System.Threading.Timer that counts down 30 seconds. Whenever server receives something from that client, it resets the timer. When timer reaches 0, it disconnects user and disposes whatever it needs to dispose. Clients application also has a timer and when user doesn't send anything for 15 seconds (half of the server's value, just to be sure), it sends the keepalive message.
My question is, is there easier way to achieve this? Maybe some option on TcpClient? I tried with TcpClient.ReceiveTimeout, but that doesn't seem to work with ReadAsync.
As Stephen points out using heartbeat messages in the application protocol is the only surefire method of ensuring that the connection is alive and that both applications are operating correctly. be warned that many an engineer has created a heartbeat thread that continues to operate even when the application threads have failed.
Using the classes here will solve your asynchronous socket question.
public sealed class SocketAwaitable : INotifyCompletion
{
private readonly static Action SENTINEL = () => { };
internal bool m_wasCompleted;
internal Action m_continuation;
internal SocketAsyncEventArgs m_eventArgs;
public SocketAwaitable(SocketAsyncEventArgs eventArgs)
{
if (eventArgs == null) throw new ArgumentNullException("eventArgs");
m_eventArgs = eventArgs;
eventArgs.Completed += delegate
{
var prev = m_continuation ?? Interlocked.CompareExchange(
ref m_continuation, SENTINEL, null);
if (prev != null) prev();
};
}
internal void Reset()
{
m_wasCompleted = false;
m_continuation = null;
}
public SocketAwaitable GetAwaiter() { return this; }
public bool IsCompleted { get { return m_wasCompleted; } }
public void OnCompleted(Action continuation)
{
if (m_continuation == SENTINEL ||
Interlocked.CompareExchange(
ref m_continuation, continuation, null) == SENTINEL)
{
Task.Run(continuation);
}
}
public void GetResult()
{
if (m_eventArgs.SocketError != SocketError.Success)
throw new SocketException((int)m_eventArgs.SocketError);
}
}
I'm using ActiveMQ 5.6.0 and ActiveMQ NMS client.
I connect to the broker using the follow code:
var connectionFactory = new ConnectionFactory(
"failover:(tcp://localhost:61616)?transport.timeout=5000"
);
connection = connectionFactory.CreateConnection();
connection.Start();
connection.ConnectionResumedListener += OnConnectionResumed;
Then I stop the broker and start it again. After that in the method OnConnectionResumed
private void OnConnectionResumed()
{
var session = connection.CreateSession();
...
}
I always get the failover timeout exception when try to create a session.
What am I doing wrong?
Thanks
This problem appeared because I had created a session in the thread where the
resumed call is made in.
The correct code is:
private void OnConnectionResumed()
{
Task.Factory.StartNew(() =>
{
var session = connection.CreateSession();
...
});
}
I am trying to connect to my server with a TcpClient.BeginConnect / TcpClient.EndConnect combo. However, some things don't work as they should.
The scenario is as follows:
Call to the TcpClient.BeginConnect
Server is intentionally offline (for testing purposes) - thus no connection can be made.
I close the application (client.Close() gets called in the process which closes the socket which in turn stops the async operation)
TcpClient connection callback method happens giving IAsyncResult
Call to the TcpClient.EndConnect method with the given IAsyncResult
NullReferenceException happens on EndConnect (?)
Since the last form (window) was closed, the app should exit - however it does not, at least not until BeginConnect operation completes (which is strange, as callback has already been called).
What happens here is that a NullReferenceException is caught. As you can see from the picture above, neither client nor ar are null. The problem is that the MSDN documentation for the EndConnect does not mention the case in which this exception is thrown.
So basically, I have no idea what is going on. The problem is that I am forced to wait for the app to close (as if the connection operation still waits for a timeout). If a server is online, it connects and disconnects just fine.
What does NullReferenceException in this context mean? How to avoid BeginConnect operation to block the application closing in case the connection can't be established?
Additional notes (requested in comments):
Here is the code to create the client (client is a member variable:
public void Connect()
{
try
{
lock (connectionAccess)
{
if (State.IsConnectable())
{
// Create a client
client = new TcpClient();
client.LingerState = new LingerOption(false, 0);
client.NoDelay = true;
State = CommunicationState.Connecting;
client.BeginConnect(address, port, onTcpClientConnectionEstablished, null);
}
else
{
// Ignore connecting request if a connection is in a state that is not connectable
}
}
}
catch
{
Close(true);
}
}
Also the Close method:
public void Close(bool causedByError)
{
lock (connectionAccess)
{
// Close the stream
if (clientStream != null)
clientStream.Close();
// Close the gateway
if (client != null)
client.Close();
// Empty the mailboxes
incomingMailbox.Clear();
outgoingMailbox.Clear();
State = causedByError ? CommunicationState.CommunicationError : CommunicationState.Disconnected;
}
}
The NullReferenceException is probably due to TcpClient.Client being null.
If you were to follow the MSDN Example for TcpClient.BeginConnect and pass theTcpClient object as the state object:
private void onConnEst(IAsyncResult ar)
{
try
{
TcpClient client = (TcpClient)ar.AsyncState;
if(client!=null && client.Client!=null)
{
client.EndConnect(ar);
}
}
catch(Exception ex){...}
}
This should handle the case when Close() is called before the Callback.
Going back to your problem - how long does it take for the application to eventually close?
This obviously a bug inside the TcpClient class. I have also faced it. TcpClient.Dispose may set Client field to null but EndConnect does not expect that.
I had a similar error and ended up using this code. I am not sure if it will hold with the IASyncResult interface, but there may be a similar way to run this check. I do notice that your ar.AsyncState == null, so perhaps try starting there, i.e. is it null when you connect properly?
private void connConnectCompleted(AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
// Something didn't work...abort captain
CloseSocket();
Console.WriteLine(this.GetType().ToString() + #":Error connecting socket:" + e.Error.Message);
return;
}
// Do stuff with your connection
}
EDIT: Sorry I didn't realise I didn't post what generated my AsyncCompletedEventArgs, which is more related to what you are doing. You will see the reason why I was wondering as to ar.AsyncState being null.
private void OnConnect(IAsyncResult asyncResult)
{
if (OnConnectCompleted == null) return; // Check whether something is using this wrapper
AsyncCompletedEventArgs args;
try
{
Socket outSocket = (Socket) asyncResult.AsyncState;
// Complete connection
outSocket.EndConnect(asyncResult);
args = new AsyncCompletedEventArgs(null);
OnConnectCompleted(this, args);
}
catch (Exception e)
{
args = new AsyncCompletedEventArgs(e.Message);
OnConnectCompleted(this, args);
}
}
This is a know bug.
You should be receiving 'ObjectDisposedException' instead of 'NullReferenceException'.