I connect to a client using TcpClient.BeginConnect where i have a loop that is running as long as a CancellationToken isn't requested cancelled.
while (!_token.IsCancellationRequested)
{
var s = reader.ReadLine() ?? string.Empty;
}
I also reconnect and handle exceptions with logging.
When I start this application everything works as expected. However, when I put my computer to sleep and wake it up again it seems all the threads have terminated.
The workflow is as follows:
I start (from the main thread) a new Task which executes DoWork. I execute like this: Task.Run(()=>DoWork(),_token);
DoWork instantiates a new TcpClient and initiates a BeginConnect like this:
(_client = new TcpClient()).BeginConnect(Address, Port, ConnectCallback, _client);
Within ConnectCallback I have a while statement which keeps reading data from the stream (see above).
Any idea what happens to the threads when the computer goes to sleep?
When your computer wakes from a sleep, the connections will sometimes (but not always) be severed. It depends on a number of factors, some of which might not be under your control.
In terms of your callback method, there is no exception triggered but reader.EndOfStream will be true when the connection has ended and all prior data has been read. Edit: However, if the TCP stack is unaware that the remote has disconnected, this call will block until data arrives, the TcpClient.ReceiveTimeout period has passed, or until the TCP session idle timeout, whichever happens first.
(Edit: If no data is sent, the connection will only end automatically if the TCP stack is aware (based on its own network state detection, or TCP packets) that the remote has disconnected; otherwise it will wait until session idle timeout, which can take up to an hour or so, depending on the client. One solution is for the client to send data regularly (if it has not received data recently) to act as a kind of early disconnection detection.)
In fact when the connection ends (e.g. after your computer wakes up), the while loop you posted would go into a tight loop, maxing out a CPU core, because reader.ReadLine() keeps returning null. You can check for that and break out of the loop:
if (reader.EndOfStream)
{
break;
}
The solution to this problem (although not a nice one) was to use a timer which explicitly closed the connection after a given amount of time.
var timeoutTimer = new Timer(state =>
{
var temp = (TcpClient)state;
Log.Error("Read timeout. Closing connection.");
temp.Close();
}, client, Timeout.Infinite, Timeout.Infinite);
Prior to accessing the stream I activate this timer:
timeoutTimer.Change(20000, Timeout.Infinite); // Handle timeouts.
And reset it afterwards:
timeoutTimer.Change(Timeout.Infinite, Timeout.Infinite); // Reset timeout.
Both using reader.EndOfStream or reader.ReadLine causes the thread to stop at that point, which isn't resolved unless the connetion is forcefully terminated.
EDIT:
Setting TcpClient.ReceiveTimeout does the same thing as above - probably better. Throws an IOException when the reciever does not receive any data for the specified amount of time (in ms).
Related
I am using NetworkStream with TcpClient.
First I setup my tcp client:
tcp = new TcpClient(AddressFamily.InterNetwork)
{ NoDelay = true, ReceiveTimeout = 5000};
My main data-receiving loop:
while (true)
{
//read available data from the device
int numBytesRead = await ReadAsync();
Console.WriteLine($"{numBytesRead} bytes read"); //BP2
}
And the actual TCP data reading:
public Task<int> ReadAsync()
{
var stream = tcp.GetStream();
return stream.ReadAsync(InBuffer, 0, InBuffer.Length); //BP1
}
I have this connected to a testbed which lets me send manual packets. Through setting breakpoints and debugging I have checked that stream.ReadTimeout takes the value 5000 from tcp.
If I send data frequently it all works as expected. But if I don't send any data, nothing appears to happen after 5s, no timeout. I see breakpoint BP1 being hit in the debugger but until I send data from my testbed, BP2 is not hit. I can leave it a minute or more and it just seems to sit waiting, but receives data sent after a minute, which appears to be incorrect behavior. After 5 seconds something should happen, surely (an exception as I understand it)?
It's late so I am expecting something really basic but can anyone see what my mistake is and a resolution?
Addendum
OK so when I RTFM for the actual .Net version I'm using (how may times have I been caught out by MS defaulting to .Net Core 3, I did say it was late) I see in the remarks sectio for ReadTimeout:
This property affects only synchronous reads performed by calling the
Read method. This property does not affect asynchronous reads
performed by calling the BeginRead method.
I'm unclear now if I can use modern awaitable calls at all to read socket data safely and with a timeout specifically. It's working except for the timeout but I'm not sure how given ReadAsync has no override in NetworkStream. Must I do some ugly hack or is there a simple solution?
In my case 5000 is the longest I can expect not to receive data before concluding there is a problem - the protocol has no ping mechanism so if nothing appears I assume the connection is dead. Hence thinking an Async read with a 5000ms timeout would be nice and neat.
Timeout values for network objects apply only to synchronous operations. For example, from the documentation:
This option applies to synchronous Receive calls only.
For Socket.ReceiveTimeout, TcpClient.ReceiveTimeout, and NetworkStream.ReadTimeout, the implementations all ultimately result in a call to SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, ...) which in turn is effectively calling the native setsockopt() function. From that documentation:
SO_RCVTIMEO DWORD Sets the timeout, in milliseconds, for blocking receive calls.
(emphasis mine)
It's this limitation in the underlying native API that is the reason for the same limitation in the managed API. Timeout values will not apply to asynchronous I/O on the network objects.
You will need to implement the timeout yourself, by closing the socket if and when the timeout should occur. For example:
async Task<int> ReadAsync(TcpClient client, byte[] buffer, int index, int length, TimeSpan timeout)
{
Task<int> result = client.GetStream().ReadAsync(buffer, index, length);
await Task.WhenAny(result, Task.Delay(timeout));
if (!result.IsCompleted)
{
client.Close();
}
return await result;
}
Other variations on this theme can be found in other related questions:
NetworkStream.ReadAsync with a cancellation token never cancels
Cancel C# 4.5 TcpClient ReadAsync by timeout
Closing the socket is really all that you can do. Even for synchronous operations, if a timeout occurs the socket would no longer be usable. There is no reliable way to interrupt a read operation and expect the socket to remain consistent.
Of course, you do have the option of prompting the user before closing the socket. However, if you were to do that, you would implement the timeout at a higher level in your application architecture, such that the I/O operations themselves have no awareness of timeouts at all.
When one thread is blocked by a call to TcpListener.AcceptTcpClient() and the TcpListener is Stop()'d by a second thread, the expected behavior is a SocketException thrown from the call to AcceptTcpClient().
This seems to be affected by a call to Process.Start(StartupInfo) when the created process's input stream is redirected. This can be shown by the code below.
void Main() {
TcpListener server = new TcpListener(IPAddress.Any, 1339);
server.Start();
Stopwatch sw = new Stopwatch();
Thread t = new Thread(() => {
Thread.Sleep(1000);
String cmdExe = Environment.ExpandEnvironmentVariables(#"%SYSTEMROOT%\system32\cmd.exe");
ProcessStartInfo info = new ProcessStartInfo(cmdExe, "/Q");
// This problem does not show up when this true
info.UseShellExecute = false;
// The exception is only delayed when this is false
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
Process p = Process.Start(info);
server.Stop();
//Start a timer as soon as the server is stopped
sw.Start();
});
t.Start();
try {
server.AcceptTcpClient();
}
catch (Exception) { }
// Print how long between the server stopping and the exception being thrown
sw.Stop();
sw.Elapsed.Dump();
}
When UseShellExecute is true, everything works as expected. When UseShellExecute is false, there is a delay between stopping the listener and the exception being thrown of ~25 - 30 seconds. When UseShell Execute is false and RedirectStandardInput is true, the exception seems to never be thrown until the process is terminated.
After the call to Stop(), the debugger shows that tthe listener really is stopped and the socket is no longer bound. But any incomming connection throws a different SocketExceptions saying that the "Object is not a socket".
I have solved the problem by switching to async methods which seem unaffected by all this, but I cannot wrap my head around how these two calls are connected.
Update
Using the information provided by RbMm below, I have re-solved the problem by changing the inherit flags of the listening socket. The flags used to create the socket is hard coded into the framework, however the inherit flag can be changed by p/Invoking SetHandleInformation() immediately after creating the listener. Note that a new socket is created anytime Stop() is called, so this will need to be called again if the listener is to ever be restarted.
TcpListener server = new TcpListener(IPAddress.Any, 1339);
SetHandleInformation(server.Server.Handle, HANDLE_FLAGS.INHERIT, HANDLE_FLAGS.None);
server.Start();
the TcpListener.AcceptTcpClient begin I/O request on socket file object. internally IRP allocated and associated with this file object.
the TcpListener.Stop close the socket file handle. when the last handle for a file is closed - the IRP_MJ_CLEANUP handler is called. DispatchCleanup routine cancel every IRPs (I/O requests) that associated with file object for which cleanup is called.
so usually exist only one file (socket) handle which you use in call AcceptTcpClient. and when Stop is called (before client connect) - it close this handle. if handle is single - this is last handle closed, as result all I/O request for it canceled and AcceptTcpClient finished with error (canceled).
but if handle to this socket will be duplicated - close not last handle in Stop not give effect, I/O will be not canceled.
how and why socket handle is duplicated ? by unknown reason all socket handles is inheritable by default. only begin from Windows 7 with SP1 added flag WSA_FLAG_NO_HANDLE_INHERIT which let create a socket that is non-inheritable.
until you not call CreateProcess with bInheritHandles set to true this not play role. but after such call - all inheritable handles (including all your socket handles) will be duplicated to child process.
the redirecting input stream implementation use inheritable named pipes for input/output/error stream. and started process with bInheritHandles set to true. this is have fatal effect for network code - listened socket handle is duplicated to child and Stop close not last socket handle (else one will be in child process - cmd in your case). as result AcceptTcpClient will be not finished.
the exception seems to never be thrown until the process is
terminated.
of course. when child process is terminated - the last handle to your socket will be closed and AcceptTcpClient is finished.
what is solution ? on c++ begin from win7 sp1 - always create sockets with WSA_FLAG_NO_HANDLE_INHERIT. on early systems - call SetHandleInformation for remove HANDLE_FLAG_INHERIT.
also begin from vista, when we need start child process with some duplicated handles, instead set bInheritHandles to true, which duplicated all inheritable handle to child process we can explicit set array of handles to be inherited by the child process by using PROC_THREAD_ATTRIBUTE_HANDLE_LIST.
by switching to async methods which seem unaffected by all this
no. absolute does not matter synchronous or asynchronous I/O (socket handle) you use here. the I/O request anyway will be not canceled. simply when you use synchronous call - this is very visible - call not return. if you use asynchronous call and managed environment - here more problematic note this. if you use callback, which must be called when AcceptTcpClient finished - this callback will be not called. if you associate event with this io operation - event will be not set. etc.
I have a TCPServer running for file receiving, and it works 99% of the time, but it will sometimes(randomly it seems) hang on the AcceptSocket() line even though I wait for the Pending() Flag to be true. Here is the code I am using for the server. (I am aware Thread.Sleep() is bleh, but that is not the issue at hand... I don't think).
// wait for the file connection
int elapsed = 0; bool timeout = false; int overtime = 60000;
while (fileServer.Pending() == false && (elapsed < overtime))
{
Thread.Sleep(1000);
elapsed += 1000;
if (elapsed > overtime)
timeout = true;
}
if (timeout)
{
Program.EventMessage("FILE SERVER TIMEOUT, NO FILE RECIEVED");
incoming_file = false;
continue;
}
Program.EventMessage("OPENING SOCKET");
fileSocketForClient = fileServer.AcceptSocket();
Basically, since this code does work. What would cause the issue of AcceptSocket() hanging even though it has a pending connection?
Is there anyway to check to see if the Socket Accepting is blocking that I could implement a timeout?
I'd like for the socket to remain blocking as the rest of the application does need this operation to be completed to move forward, it just needs a timeout or something so if it does crap out, can recover without having to manually restart the entire program.
Issue found, and I know what I need to do to fix it (timeout AcceptSocket()), but scrounging around SO I have not found a viable way to do so yet.
As a temporary band-aid I have moved this to multithreaded, so at least when it dies the server can still go... there will just be the forever blocking thread to deal with.
A connection was pending. Does that mean a connection will still be pending later? No. Status functions don't predict the future.
"I'm smart enough to think of every possible way a socket operation could block and make sure that doesn't happen, so I can use blocking socket operations and still be assured they'll never block." No, you aren't. Don't even try. It won't work. Use non-blocking socket operations.
I'm taking practice with the async CTP framework, and as exercise I would create a TCP client able to query a server (using an arbitrary protocol). Anyway, I'm stuck at the very early stage because an issue on the connection. Either I still didn't understand some basic point, or there is something strange.
So, here is the async connector:
public class TaskClient
{
public static Task<TcpClient> Connect(IPEndPoint endPoint)
{
//create a tcp client
var client = new TcpClient(AddressFamily.InterNetwork);
//define a function to return the client
Func<IAsyncResult, TcpClient> em = iar =>
{
var c = (TcpClient)iar.AsyncState;
c.EndConnect(iar);
return c;
};
//create a task to connect the end-point async
var t = Task<TcpClient>.Factory.FromAsync(
client.BeginConnect,
em,
endPoint.Address.ToString(),
endPoint.Port,
client);
return t;
}
}
I mean to call this function only once, then having back a TcpClient instance to use for any succeeding query (code not shown here).
Somewhere in my form, I call the function above as follows:
//this method runs on the UI thread, so can't block
private void TryConnect()
{
//create the end-point
var ep = new IPEndPoint(
IPAddress.Parse("192.168.14.112"), //this is not reachable: correct!
1601);
var t = TaskClient
.Connect(ep)
.ContinueWith<TcpClient>(_ =>
{
//tell me what's up
if (_.IsFaulted)
Console.WriteLine(_.Exception);
else
Console.WriteLine(_.Result.Connected);
return _.Result;
})
.ContinueWith(_ => _.Result.Close());
Console.WriteLine("connection in progress...");
//wait for 2" then abort the connection
//Thread.Sleep(2000);
//t.Result.Client.Close();
}
The test is to try to connect a remote server, but it has to be unreachable (PC on, but service stopped).
When I run the TryConnect function, it returns correctly "connection in progress..." as soon, then displays an exception because the remote endpoint is off. Excellent!
The problem is that it needs several seconds to return the exception, and I would like to give the chance to the user to cancel the operation in progress. According to the MSDN specs about the BeginConnect method, if you wish to abort the async operation, just call Close on the working socket.
So, I tried to add a couple of lines at the end (commented out as above), so to simulate the users cancellation after 2 seconds. The result looks as a hang of the app (hourglass). By pausing the IDE, it stops on the very last line t.Result.Client.Close(). However, by stopping the IDE everything closes normally, without any exception.
I've also tried to close the client directly as t.Result.Close(), but it's exactly the same.
It's me, or there's anything broken on the connection process?
Thanks a lot in advance.
t.Result.Close() will wait for the t task completion.
t.ContinueWith() will also wait for the completion of the task.
To cancel you must wait on 2 tasks: the tcp and a timer.
Using the async tcp syntax:
await Task.WhenAny(t,Task.Delay(QueryTimeout));
if (!t.IsCompleted)
tcpClient.Close(); //Cancel task
Try calling Dispose() on the object - it's a bit more agressive than Close(). You could look at the various Timeout members on the TcpClient class and set them to more appropriate values (e.g. 1 second in a LAN environment is probably good enough). You can also have a look at the CancellationTokenSource functionality in .Net 4.0. This allows you to signal to a Task that you wish it to discontinue - I found an article that might get you started.
You should also find out which thread is actually stalling (the primary thread might just be waiting for another thread that is stalled), e.g. the .ContinueWith(_ => _.Result.Close()) might be the problem (you should check what the behaviour is when closing a socket twice). While debugging open the Threads window (Debug -> Windows -> Threads) and have a look through each thread.
I am using the TcpClient class in C#.
Each time there is a new tcp connection request, the usual practice is to create a new thread to handle it. And it should be possible for the main thread to terminate these handler threads anytime.
My solution for each of these handler thread is as follows:
1 Check NetworkStream's DataAvailable method
1.1 If new data available then read and process new data
1.2 If end of stream then self terminate
2 Check for terminate signal from main thread
2.1 If terminate signal activated then self terminate
3 Goto 1.
The problem with this polling approach is that all of these handler threads will be taking up significant processor resources and especially so if there is a huge number of these threads. This makes it highly inefficient.
Is there a better way of doing this?
See Asynchronous Server Socket Example to learn how to do this the ".NET way", without creating new threads for each request.
Believe it or not that 1000 tick sleep will really keep things running smooth.
private readonly Queue<Socket> sockets = new Queue<Socket>();
private readonly object locker = new object();
private readonly TimeSpan sleepTimeSpan = new TimeSpan(1000);
private volatile Boolean terminate;
private void HandleRequests()
{
Socket socket = null;
while (!terminate)
{
lock (locker)
{
socket = null;
if (sockets.Count > 0)
{
socket = sockets.Dequeue();
}
}
if (socket != null)
{
// process
}
Thread.Sleep(sleepTimeSpan);
}
}
I remember working on a similar kind of Windows Service. It was a NTRIP Server that can take around 1000 TCP connections and route the data to a NTRIP Caster.
If you have a dedicated server for this application then it will not be a problem unless you add more code to each thread (File IO, Database etc - although in my case I also had Database processing to log the in/out for each connection).
The things to watch out for:
Bandwidth when the threads goes up to 600 or so. You will start seeing disconnections when the TCP Buffer window is choked for some reason or the available bandwidth falls short
The operating system on which you are running this application might have some restrictions, which can cause disconnections
The above might not be applicable in your case but I just wanted it put it here because I faced then during development.
You're right that you do not want all of your threads "busy waiting" (i.e. running a small loop over and over). You either want them blocking, or you want to use asynchronous I/O.
As John Saunders mentioned, asynchronous I/O is the "right way" to do this, since it can scale up to hundreds of connections. Basically, you call BeginRead() and pass it a callback function. BeginRead() returns immediately, and when data arrives, the callback function is invoked on a thread from the thread pool. The callback function processes the data, calls BeginRead() again, and then returns, which releases the thread back into the pool.
However, if you'll only be holding a handful of connections open at a time, it's perfectly fine to create a thread for each connection. Instead of checking the DataAvailable property in a loop, go ahead and call Read(). The thread will block, consuming no CPU, until data is available to read. If the connection is lost, or you close it from another thread, the Read() call will throw an exception, which you can handle by terminating your reader thread.