Detect client disconnect with HttpListener - c#

I have an application that uses HttpListener, I need to know when the client disconnected, right now I have all my code inside a try/catch block which is pretty ugly and not a good practice.
How can I know if a client disconnected?
thanks!

Short answer: you can't. If a client stops talking, the underlying socket may stay open and won't ever close; it'll just timeout. The way to detect this is to attempt to perform an action on that connection and if the connection is no longer valid, it'll throw some sort of exception depending on what happened. If you use HttpListener asynchronously, it may clean up your code a bit in terms of a try/catch but unfortunately that's what you're stuck with. There is no event that will fire if the client disconnects.

You can! Two options I've found are with reflection or unsafe code. Both options give you a client disconnect token from a method that looks like this:
CancellationToken GetClientDisconnectToken(HttpListenerRequest request)
To implement this via refection, I found HttpListener actually implements client disconnect notifications for the built-in authentication implementation. I created a type who derives from Hashtable, the structure HttpListener uses for outstanding client disconnect notifications, to tap in to that code outside of its intended purpose.
For every request there is a ConnectionId used by HTTP.SYS. This code uses reflection and creates a Func<> to obtain this id for any HttpListenerRequest:
private static Func<HttpListenerRequest, ulong> GetConnectionId()
{
var field = typeof(HttpListenerRequest).GetField("m_ConnectionId",
BindingFlags.Instance | BindingFlags.NonPublic);
if (null == field)
throw new InvalidOperationException();
return request => (ulong)field.GetValue(request);
}
The next bit of code is a little more complicated:
private static Func<ulong, IAsyncResult> GetRegisterForDisconnectNotification(HttpListener httpListener)
{
var registerForDisconnectNotification = typeof(HttpListener)
.GetMethod("RegisterForDisconnectNotification", BindingFlags.Instance | BindingFlags.NonPublic);
if (null == registerForDisconnectNotification)
throw new InvalidOperationException();
var finishOwningDisconnectHandling =
typeof(HttpListener).GetNestedType("DisconnectAsyncResult", BindingFlags.NonPublic)
.GetMethod("FinishOwningDisconnectHandling", BindingFlags.Instance | BindingFlags.NonPublic);
if (null == finishOwningDisconnectHandling)
throw new InvalidOperationException();
    IAsyncResult RegisterForDisconnectNotification(ulong connectionId)
{
var invokeAttr = new object[] { connectionId, null };
registerForDisconnectNotification.Invoke(httpListener, invokeAttr);
var disconnectedAsyncResult = invokeAttr[1];
if (null != disconnectedAsyncResult)
finishOwningDisconnectHandling.Invoke(disconnectedAsyncResult, null);
return disconnectedAsyncResult as IAsyncResult;
}
return RegisterForDisconnectNotification;
}
This reflection creates a Func<> whose input is a ConnectionId and returns an IAsyncResult containing the state of the inflight request. Internally, this calls a private method on HttpListener:
private unsafe void RegisterForDisconnectNotification(ulong connectionId,
ref HttpListener.DisconnectAsyncResult disconnectResult)
As the name implies, this method calls a Win32 API to be notified of a client disconnect. Immediately after, using the result of that method I call a private method on a nested type: void HttpListener.DisconnectedAsyncResult.FinishOwningDisconnectHandling(), if the connection is still open. This method changes the state of this structure from "in HandleAuthentication" to "Normal", which is the state it needs to be in to invoke the IO completion callback who will call Remove on the HashTable. Intercepting this call turns out to be pretty simple - create a derived type and override Remove:
public override void Remove(object key)
{
base.Remove(key);
var connectionId = (ulong)key;
if (!_clientDisconnectTokens.TryRemove(connectionId, out var cancellationTokenSource))
return;
Cancel(cancellationTokenSource);
}
private static void Cancel(CancellationTokenSource cancellationTokenSource)
{
// Use TaskScheduler.UnobservedTaskException for caller to catch exceptions
Task.Run(() => cancellationTokenSource.Cancel());
}
Calling Cancel tends to throw, so we invoke this using TPL so you can catch any exception thrown during cancel by subscribing to TaskScheduler.UnobservedTaskException.
What left?
creation of the HttpListenerHashtable derived type
storage of in-flight CancellationTokenSource instances
set or replace the HashTable field of HttpListener with the HttpListenerHashtable (it's best to do this right after creating the HttpListener instance)
handle the request of a disconnect token, and the client disconnects while the code is executing
All of which are addressed in the full source.

I'm having the same problem, I do a long sql query, and if user keeps pressing F5, it blows up.
HttpListener do not expose its underlying socket so you can poll it.
So I managed this workaround:
IDbCommand cmd = LongSqlQuery();
while (!taskCompleted)
{
try
{
//send disposable data at times to check connection
byte[] dummy = new byte[1] { 0xFF };
outputStream.Write(dummy,0,1);
Task.Delay(100).Wait();
}
catch
{
//Client disconnect
cmd.Cancel();
}
}
//Redirect client to results page
response.Redirect("http://yourserver.com/results?query=yourquery");

Related

Detecting dropped connections

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);
}
}

WCF TimeoutException despite stepping through showing successful return

I have two self hosted services running on the same network. The first is sampling an excel sheet (or other sources, but for the moment this is the one I'm using to test) and sending updates to a subscribed client.
The second connects as a client to instances of the first client, optionally evaluates some formula on these inputs and the broadcasts the originals or the results as updates to a subscribed client in the same manner as the first. All of this is happening over a tcp binding.
My problem is occuring when the second service attempts to subscribe to two of the first service's feeds at once, as it would do if a new calculation is using two or more for the first time. I keep getting TimeoutExceptions which appear to be occuring when the second feed is subscribed to. I put a breakpoint in the called method on the first server and stepping through it, it is able to fully complete and return true back up the call stack, which indicates that the problem might be some annoying intricacy of WCF
The first service is running on port 8081 and this is the method that gets called:
public virtual bool Subscribe(int fid)
{
try
{
if (fid > -1 && _fieldNames.LeftContains(fid))
{
String sessionID = OperationContext.Current.SessionId;
Action<Object, IUpdate> toSub = MakeSend(OperationContext.Current.GetCallbackChannel<ISubClient>(), sessionID);//Make a callback to the client's callback method to send the updates
if (!_callbackList.ContainsKey(fid))
_callbackList.Add(fid, new Dictionary<String, Action<Object, IUpdate>>());
_callbackList[fid][sessionID] = toSub;//add the callback method to the list of callback methods to call when this feed is updated
String field = GetItem(fid);//get the current stored value of that field
CheckChanged(fid, field);//add or update field, usually returns a bool if the value has changed but also updates the last value reference, used here to ensure there is a value to send
FireOne(toSub, this, MakeUpdate(fid, field));//sends an update so the subscribing service will have a first value
return true;
}
return false;
}
catch (Exception e)
{
Log(e);//report any errors before returning a failure
return false;
}
}
The second service is running on port 8082 and is failing in this method:
public int AddCalculation(string name, string input)
{
try
{
Calculation calc;
try
{
calc = new Calculation(_fieldNames, input, name);//Perform slow creation before locking - better wasted one thread than several blocked ones
}
catch (FormatException e)
{
throw Fault.MakeCalculationFault(e.Message);
}
lock (_calculations)
{
int id = nextID();
foreach (int fid in calc.Dependencies)
{
if (!_calculations.ContainsKey(fid))
{
lock (_fieldTracker)
{
DataRow row = _fieldTracker.Rows.Find(fid);
int uses = (int)(row[Uses]) + 1;//update uses of that feed
try
{
if (uses == 1){//if this is the first use of this field
SubServiceClient service = _services[(int)row[ServiceID]];//get the stored connection (as client) to that service
service.Subscribe((int)row[ServiceField]);//Failing here, but only on second call and not if subscribed to each seperately
}
}
catch (TimeoutException e)
{
Log(e);
throw Fault.MakeOperationFault(FaultType.NoItemFound, "Service could not be found");//can't be caught, if this timed out then outer connection timed out
}
_fieldTracker.Rows.Find(fid)[Uses] = uses;
}
}
}
return id;
}
}
catch (FormatException f)
{
Log(f.Message);
throw Fault.MakeOperationFault(FaultType.InvalidInput, f.Message);
}
}
The ports these are on could change but are never shared. The tcp binding used is set up in code with these settings:
_tcpbinding = new NetTcpBinding();
_tcpbinding.PortSharingEnabled = false;
_tcpbinding.Security.Mode = SecurityMode.None;
This is in a common library to ensure they both have the same set up, which is also a reason why it is declared in code.
I have already tried altering the Service Throttling Behavior for more concurrent calls but that didn't work. It's commented out for now since it didn't work but for reference here's what I tried:
ServiceThrottlingBehavior stb = new ServiceThrottlingBehavior
{
MaxConcurrentCalls = 400,
MaxConcurrentSessions = 400,
MaxConcurrentInstances = 400
};
host.Description.Behaviors.RemoveAll<ServiceThrottlingBehavior>();
host.Description.Behaviors.Add(stb);
Has anyone had similar issues of methods working correctly but still timing out when sending back to the caller?
This was a difficult problem and from everything I could tell, it is an intricacy of WCF. It cannot handle one connection being reused very quickly in a loop.
It seems to lock up the socket connection, though trying to add GC.Collect() didn't free up whatever resources it was contesting.
In the end the only way I found to work was to create another connection to the same endpoint for each concurrent request and perform them on separate threads. Might not be the cleanest way but it was all that worked.
Something that might come in handy is that I used the svc trace viewer to monitor the WCF calls to try and track the problem, I found out how to use it from this article: http://www.codeproject.com/Articles/17258/Debugging-WCF-Apps

Socket connection issues

My answer:
After getting annoyed, I have found a solution. The problem was indeed C# either C#'s garbage collector or C#'s multithreading, it probably thought the object was no longer needed within THAT thread, and deleted it. The solution was found as follows:
I implemented the ClientThread into the Server class, passing the Client object as a parameters, this minor change made it work. Thank you for all your responses, if anyone in the future has this problem maybe it wasn't C#'s garbage collector. But C# mutithreading OR networking must be done within the same class. I kept my client class and just made the thread object run the function within the Server class.
If anyone can figure out what my problem was, feel free to comment so I can expand my little knowledge of C#'s memory management.
Thanks again to all the people who attempted to help me in this thread.
Original Question
I'm a C++ programmer so I'm used to managing memory myself, and I'm really not sure how to solve this problem.
For instance in C++:
while(true)
{
void* ptr = new char[1000];
}
This would be an obvious memory leaking program, so I need to go ahead and clean it up with:
delete ptr;
But there are cases when I want to create memory for use in a different thread and I DO NOT WANT IT DELETED AFTER THE LOOP.
while(true)
{
socket.Accept(new Client());
}
//////////Client Constructor////////////
Client()
{
clientThread.Start();
}
This snippet is basically what I want to do in C#, but my client connects then disconnects immediately, I'm assuming this is because at the end of the while loop my new Client() is being deleted by our favorite Garbage Collector.
So my question is, how do I get around this and make it NOT delete my object.
Many have replied saying various things about having other links to it in the code. I forgot to mention that I also save the new client in a list of clients located globally
List<Client> clients;
//inside loop
clients.Add(new Client(socket.Accept()));
Ok because I'm unsure if I'm missing more information here is the ACTUAL code snippet
// Server class
internal Socket socket { get; set; }
internal Thread thread { get; set; }
internal List<Client> clients { get; set; }
internal void Init()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
thread = new Thread(AcceptThread);
}
internal void Start(int port,List<Client> clients)
{
var ipep = new IPEndPoint(IPAddress.Any, port);
this.socket.Bind(ipep);
this.socket.Listen(10);
this.clients = clients;
this.thread.Start();
}
internal void End()
{
socket.Close();
thread.Abort();
}
internal void AcceptThread()
{
int ids = 0;
while (true)
{
Client client = new Client();
client.Init(socket.Accept());
client.clientid = ids++;
client.Start();
clients.Add(client);
}
}
// Client class
public class Client
{
.....
#region Base Code
internal void Init(Socket socket)
{
this.socket = socket;
this.status = new SocketStatus();
this.thread = new Thread(ClientThread);
this.stream = new Stream();
}
internal void Start()
{
thread.Start();
}
internal void Close()
{
socket.Close();
status = SocketStatus.Null;
thread.Abort();
}
internal void ClientThread()
{
try
{
while (true)
{
byte[] data = new byte[1];
int rec = socket.Receive(data);
if (rec == 0)
throw new Exception();
else
stream.write(data[0]);
}
}
catch(Exception e)
{
Close();
}
}
#endregion
}
I thank you for all your replies.
That's not how it works at all. If there exists any reference to the instance of Client you created, it is not garbage-collected. This doesn't just apply to your own code, either. Therefore, if GCing is indeed the source of your issue, you never could have accessed it in the first place!
If you weren't intending to access it, you can hold on to them anyway by putting them in a List. However, I believe that once you actually use them in the other thread you're talking about, your problems will go away.
I've been out of the c# game for a while but I don't see anything immediately wrong there. Garbage collection shouldn't kick in until objects are actually not referenced anymore. if your socket.Accept() doesn't keep a reference, perhaps you could do this manually:
var clients = new List<Client>();
while(true)
{
client = new Client();
clients.Add(client);
socket.Accept(client);
}
////////// Client Constructor ////////////
Client()
{
clientThread.Start();
}
From MSDN:
If no data is available for reading, the Receive method will block until data is
available, unless a time-out value was set by using
Socket.ReceiveTimeout. If the time-out value was exceeded, the Receive
call will throw a SocketException. If you are in non-blocking mode,
and there is no data available in the in the protocol stack buffer,
the Receive method will complete immediately and throw a
SocketException. You can use the Available property to determine if
data is available for reading. When Available is non-zero, retry the
receive operation.
If you are using a connection-oriented Socket, the Receive method will
read as much data as is available, up to the size of the buffer. If
the remote host shuts down the Socket connection with the Shutdown
method, and all available data has been received, the Receive method
will complete immediately and return zero bytes.
This appears to be the only way to get a 0 return value from the Receive method, and not get an exception, so it would appear that whatever is on the other end is closing the connection.
The garbage collector only deletes resources that aren't reachable through any reference in your program. As long as you still have a variable that refers to the object, it'll continue to exist.

TcpClient.EndConnect throws NullReferenceException when socket is closed

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'.

HttpListeners and ports

I am creating an HttpListener by attempting to grab a random port that is open (or one that is not in IpGlobalProperties.GetActiveTcpConnections()). The issue I am running into is that after a while of making these connections and disposing them I am getting this error : No more memory is available for security information updates
Is there any way to resolve this or is there a proper way of getting rid of HttpListeners. I am just calling listener.Close().
Here is the method used to create the listeners :
private HttpListener CreateListener()
{
HttpListener httpListener;
DateTime timeout = DateTime.Now.AddSeconds(30);
bool foundPort = false;
do
{
httpListener = new HttpListener();
Port = GetAvailablePort();
string uriPref = string.Format("http://{0}:{1}/", Environment.MachineName.ToLower(), Port);
httpListener.Prefixes.Add(uriPref);
try
{
httpListener.Start();
foundPort = true;
break;
}
catch
{
httpListener.Close();
FailedPorts.Add(Port);
}
} while (DateTime.Now < timeout);
if (!foundPort)
throw new NoAvailablePortException();
return httpListener;
}
Have you tried calling listener.Stop() before Close()?
Another thing to try is to wrap your code in a using() {} block to make sure your object is disposed properly.
Finally, what are you doing with the listener (a code snippet might help)? Are you leaving any streams open?
This is the hackish way to force HttpListener to unregister all your Prefixes associated with that httpListener (this uses some of my custom reflection libraries but the basic idea is the same)
private void StopListening()
{
Reflection.ReflectionHelper.InvokeMethod(httpListener, "RemoveAll", new object[] {false});
httpListener.Close();
pendingRequestQueue.Clear(); //this is something we use but just in case you have some requests clear them
}
You need to remove the failed prefix before adding a new one, which is a lot simpler then Jesus Ramos proposed.
httpListener.Prefixes.Remove(uriPref);

Categories