Skip to content

TcpListener.Stop hangs if Accept is in progress #24513

@psantosl

Description

@psantosl

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:

  • We start the server.
  • We try to stop it.
  • It takes forever to finish.
  • Attaching the debugger (from Visual Studio on Windows, which is awesome :P) we see the process is stopped in TcpListener.Stop and never leaves.

Attached a repro case, but the code is super simple:

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace tcplistenertest
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread listenerThread = StartBackgroundThread(new ThreadStart(ThreadProc));

            while(Console.ReadLine() != string.Empty)
            {
            }

            mListener.Stop();
            listenerThread.Join();
        }

        static void ThreadProc()
        {
            try
            {
                mListener = StartListening();

                AcceptLoop(mListener);
            }
            catch(Exception e)
            {
                Console.WriteLine($"ThreadProc error: {e.Message}");
                Console.WriteLine($"Stack trace:{Environment.NewLine}{e.StackTrace}");
            }
        }

        static Thread StartBackgroundThread(ThreadStart task)
        {
            Thread thread = new Thread(task);
            thread.IsBackground = true;
            thread.Start();
            return thread;
        }

        static TcpListener StartListening()
        {
            TcpListener result = new TcpListener(IPAddress.Any, PORT);
            result.Start();

            return result;
        }

        static 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);
                    Console.WriteLine($"Connection accepted from {socket.RemoteEndPoint}");
                }
                catch (SocketException e)
                {
                    Console.WriteLine($"Socket error accepting connection: {e.Message}");
                    Console.WriteLine($"Stack trace:{Environment.NewLine}{e.StackTrace}");
                    break;
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Generic error accepting connection: {e.Message}");
                    Console.WriteLine($"Stack trace:{Environment.NewLine}{e.StackTrace}");
                    break;
                }

                try
                {
                    socket.Close();
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Error closing socket: {e.Message}");
                    Console.WriteLine($"Stack trace:{Environment.NewLine}{e.StackTrace}");
                    break;
                }
            }

            Console.WriteLine("Accept loop thread stopping");
        }

        static Socket AcceptConnection(TcpListener listener)
        {
            Socket socket = listener.AcceptSocket();
            try
            {
                socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.Debug, 1);
                LingerOption optionValue = new LingerOption(true, 3);

                socket.SetSocketOption(SocketOptionLevel.Socket,
                    SocketOptionName.Linger, optionValue);

                return socket;
            }
            catch (Exception)
            {
                socket.Close();
                throw;
            }
        }

        static TcpListener mListener;
        const int PORT = 55000;
    }
}

tcplistenertest-1.zip

Thanks!
pablo

[EDIT] Add C# syntax highlighting by @karelz

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions