How to gracefully close an Async Server Socket? C#
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!
Aelphaeis
Updated on June 06, 2022Comments
-
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 over 9 yearsAre 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 over 9 yearsClose() 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 over 9 yearsI think it would be better to add 'ear.Close()' after the while loop. This way the socket won't get closed mid-job.
-
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 almost 8 yearsThe only issue I can see is StartListening will now block, the example isn't really async.
-
Aelphaeis over 5 years@TonyEdgecombe startListening is private and can only be accessed from start.