How to gracefully close an Async Server Socket? C#

15,611

So Here is how I fixed it:

First I added a Boolean called IsListening.

Next I changed condition that keeps the ear listening to While(IsListening).

In the stop method, I put set the IsListening variable to false before calling ear.close() and lastly I do a check for the ear inside of the AcceptCallback after the Manual SetEvent.

The end result looks like this:

    private static ManualResetEvent allDone = new ManualResetEvent(false);
    private Socket ear;
    public void Start()
    {
        new Thread(() => StartListening()).Start();
    }

    private void StartListening()
    {
        IP = Dns.GetHostAddresses(Dns.GetHostName()).Where(address => address.AddressFamily == AddressFamily.InterNetwork).First();
        port = 11221;
        IPEndPoint localEndPoint = new IPEndPoint(IP, Port);
        ear = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        ear.Bind(localEndPoint);
        ear.Listen(100);

        IsListening = true;

        while (IsListening)
        {
            allDone.Reset();
            ear.BeginAccept(new AsyncCallback(AcceptCallback), ear);
            allDone.WaitOne();
        }
        Console.WriteLine("Socket Closed");
    }

    private void AcceptCallback(IAsyncResult ar)
    {
        allDone.Set();
        if (IsListening == false) return;
        Socket listener = (Socket)ar.AsyncState;
        Socket handler = listener.EndAccept(ar);

        StateObject state = new StateObject();
        state.workSocket = handler;
        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
    }
    public static void ReadCallback(IAsyncResult ar) 
    {
        String content = String.Empty;

        StateObject state = (StateObject) ar.AsyncState;
        Socket handler = state.workSocket;

        int bytesRead = handler.EndReceive(ar);

        if (bytesRead > 0) {
            state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));

            content = state.sb.ToString();
            if (content.IndexOf("<EOF>") > -1) {
                Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content );
            } 
            else 
            {
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReadCallback), state);
            }
        }
    }

    public void Stop()
    {
        IsListening = false;
        ear.Close();
    }

The exception is now gone. I hope this helps someone else!

Share:
15,611
Aelphaeis
Author by

Aelphaeis

Updated on June 06, 2022

Comments

  • Aelphaeis
    Aelphaeis almost 2 years

    I've seen a lot of questions about handling Sockets without object disposed exceptions, so I've decided to take a crack at it and see if it could be done. Here are my findings.

    Problem?

    You have a piece of code that uses a Socket from System.Net.Sockets that is a server socket. The problem is you want to close the socket and every time you try you get an ObjectDisposedException.

    Your code may look something like this:

        private static ManualResetEvent allDone = new ManualResetEvent(false);
        private Socket ear;
        public void Start()
        {
            new Thread(() => StartListening()).Start();
        }
    
        private void StartListening()
        {
            IP = Dns.GetHostAddresses(Dns.GetHostName()).Where(address => address.AddressFamily == AddressFamily.InterNetwork).First();
            port = 11221;
            IPEndPoint localEndPoint = new IPEndPoint(IP, Port);
            ear = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            ear.Bind(localEndPoint);
            ear.Listen(100);
            try
            {
    
                while (true)
                {
                    allDone.Reset();
                    ear.BeginAccept(new AsyncCallback(AcceptCallback), ear);
                    allDone.WaitOne();
                }
            }
            catch (ObjectDisposedException e)
            {
                Console.WriteLine("Socket Closed");
            }
        }
    
        private void AcceptCallback(IAsyncResult ar)
        {
            allDone.Set();
            Socket listener = (Socket)ar.AsyncState;
            Socket handler = listener.EndAccept(ar);
    
            StateObject state = new StateObject();
            state.workSocket = handler;
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
        }
        public static void ReadCallback(IAsyncResult ar) 
        {
            String content = String.Empty;
    
            StateObject state = (StateObject) ar.AsyncState;
            Socket handler = state.workSocket;
    
            int bytesRead = handler.EndReceive(ar);
    
            if (bytesRead > 0) {
                state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
    
                content = state.sb.ToString();
                if (content.IndexOf("<EOF>") > -1) {
                    Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content );
                } 
                else 
                {
                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                    new AsyncCallback(ReadCallback), state);
                }
            }
        }
    
        public void Stop()
        {
            ear.Close();
        }
    

    Running a Code similar to above will give you an error in the AcceptCallback went trying to end the receive. Even if you catch that, you still get an error in the StartListening() Method.

  • Carlos
    Carlos over 9 years
    Are you sure this works, and is not simply winning a race condition? What happens if you check the bool and another thread runs stop() before your operation?
  • Aelphaeis
    Aelphaeis over 9 years
    Close() sends a message to the ear before closing. So when you use Stop() you leave the loop completely. It is undocumented for some reason but it does. Perhaps later I'll make a full example and post back for you to see.
  • Ilya Suzdalnitski
    Ilya Suzdalnitski over 9 years
    I think it would be better to add 'ear.Close()' after the while loop. This way the socket won't get closed mid-job.
  • Aelphaeis
    Aelphaeis over 9 years
    @IlyaSuzdalnitski No, you don't want to do that because ear.Close() has already been called. That is the only way that IsListening can be set to false.
  • Tony Edgecombe
    Tony Edgecombe almost 8 years
    The only issue I can see is StartListening will now block, the example isn't really async.
  • Aelphaeis
    Aelphaeis over 5 years
    @TonyEdgecombe startListening is private and can only be accessed from start.