New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TcpListener.Stop hangs if Accept is in progress #24513
TcpListener.Stop hangs if Accept is in progress #24513
Comments
In the meantime, is there any workaround we can use? I guess we can use the async code instead? Or do we need to go for bare sockets? Any of the two would be fine, we want to get ready to release Plastic SCM in .NET Core :) |
I'll take a look @psantosl |
The problem with the code @psantosl is that the listener.AcceptSocket() is BLOCKING call. I think your best option is using the async code as you suspected. static async void AcceptLoop(TcpListener listener)
{
// this is the main loop of the Socket based server
while (true)
{
Socket socket = null;
try
{
Console.WriteLine("Accepting connection...");
//socket = AcceptConnection(listener);
socket = await listener.AcceptSocketAsync(); That does does not block the thread so when you ask for Stop() everything will terminate. |
Thanks @wfurt, I will try the async code you suggest. I wonder if using a socket instead of tcplistener would work too (guess it won't) Thanks |
The problem is that the close() does not happens because socket blocks for Accept(). static void Main(string[] args)
{
Thread listenerThread = StartBackgroundThread(new ThreadStart(ThreadProc));
while(Console.ReadLine() != string.Empty)
{
}
mListener.Server.Shutdown(SocketShutdown.Both);
mListener.Stop();
listenerThread.Join();
} The shutdown will cause Accept() to fail and that breaks the blocking. |
You can use async version like this public class TcpServer
{
#region Public.
/// <summary>
/// Create new instance of TcpServer.
/// </summary>
/// <param name="ip">Ip of server.</param>
/// <param name="port">Port of server.</param>
public TcpServer(string ip, int port)
{
_listener = new TcpListener(IPAddress.Parse(ip), port);
}
/// <summary>
/// Starts receiving incoming requests.
/// </summary>
public void Start()
{
_listener.Start();
_ct = _cts.Token;
_listener.BeginAcceptTcpClient(ProcessRequest, _listener);
}
/// <summary>
/// Stops receiving incoming requests.
/// </summary>
public void Stop()
{
//If listening has been cancelled, simply go out from method.
if(_ct.IsCancellationRequested)
{
return;
}
//Cancels listening.
_cts.Cancel();
//Waits a little, to guarantee that all operation receive information about cancellation.
Thread.Sleep(100);
_listener.Stop();
}
#endregion
#region Private.
//Process single request.
private void ProcessRequest(IAsyncResult ar)
{
//Stop if operation was cancelled.
if(_ct.IsCancellationRequested)
{
return;
}
var listener = ar.AsyncState as TcpListener;
if(listener == null)
{
return;
}
//Check cancellation again. Stop if operation was cancelled.
if(_ct.IsCancellationRequested)
{
return;
}
//Starts waiting for the next request.
listener.BeginAcceptTcpClient(ProcessRequest, listener);
//Gets client and starts processing received request.
using(TcpClient client = listener.EndAcceptTcpClient(ar))
{
var rp = new RequestProcessor();
rp.Proccess(client);
}
}
#endregion
#region Fields.
private CancellationToken _ct;
private CancellationTokenSource _cts = new CancellationTokenSource();
private TcpListener _listener;
#endregion
} https://github.com/avgoncharov/how_to/blob/master/how_to/SimpleTcpServer/TcpServer.cs |
Reactivating since we reverted PR dotnet/corefx#37486. |
I have a DotNet Core 2.2 Console App. When targeting Windows, mListener.Stop() correctly cancels the in-progress tcpListener.AcceptTcpClient() blocking call. When targeting Linux Docker, mListener.Stop() hangs. (Note, I do not have the option of switching to the BeginAcceptTcpClient() async version at this time as flamencist suggests). Under Linux Docker, I can add mListener.Server.Shutdown(SocketShutdown.Both) before the mListener.Stop() as wfurt suggests, and the tcpListener.AcceptTcpClient() will be correctly canceled. However, under Windows, calling mListener.Server.Shutdown(SocketShutdown.Both) before mListener.Stop() throws an error. Catch-22 :-( So what works for me, to correctly cancel the synchronous cpListener.AcceptTcpClient() blocking call under both Windows and Linux Docker, is: But of course I would prefer that the same code worked correctly and in the same way under both Windows and Linux Docker, thanks. |
Just catch the error for now? |
Yes, that's what I have done, but the inelegance of the code offends both my OCD and my sense of professionalism, LOL. Seriously, I am amazed at how well the DotNet Core library works so well and effortlessly under both Windows and Linux Docker, and would love to see C# working perfectly and consistently in all supported environments. |
note that this will be fixed in 3.0 once dotnet/corefx#38804 is merged. |
Hi, This is still open, correct?? The problem we have is that we are using websocket-sharp, and they use a listener.Close without running a Shutdown first and... well, it hangs forever on Linux 😥 |
@psantosl this has been fixed in 5.0 - see bug history and milestone. |
Hi,
We are experiencing an issue on Linux, building with publish -r linux-x64.
It looks like TcpListener.Stop() hangs when we try to orderly shutdown the server.
The socket is waiting for an Accept, then other thread does TcpListener.Stop().
The same code works fine on Windows (.NET Core) and also on Linux/Mono.
Here the sequence is:
Attached a repro case, but the code is super simple:
tcplistenertest-1.zip
Thanks!
pablo
[EDIT] Add C# syntax highlighting by @karelz
The text was updated successfully, but these errors were encountered: