Re: Question on Socket disconnect handling



I might as well post the reference count mechanism I was talking about.
It's inspired from a reference counting technique that's useful when
unloading Plug N Play device drivers. It's a shame such a mechanism is
not part of the classes in the .NET Framework that have asynchronous
callbacks.

Anyone who believes in the orderly shutdown of asynchronous callbacks
can do something like this. (Anyone who does not believe in orderly
shutdown or resource acquisition and release should please stop
developing software.)

(www.developersdex.com seems to make words like `List' and
`ArraySegment' lower case when they shouldn't be below)


using System;
using System.Collections.Generic;
using System.Threading;
using System.Net.Sockets;

namespace Solution
{
class Program
{
static void SendCallback(IAsyncResult ar)
{
Console.WriteLine("SendCallback entered");

SendCallBackArgs args =
(SendCallBackArgs)ar.AsyncState;
try
{
#if true
args.Socket.EndSend(ar);

// simulate timing that can cause callback
// and Close to race
Thread.Sleep(1000);

args.Event.Set();
#else
// simulate timing that can cause callback
// and Close to race
Thread.Sleep(1000);

args.Event.Set();

args.Socket.EndSend(ar);
#endif
}
catch(ObjectDisposedException ex)
{
Console.WriteLine("Error: " + ex.Message);
}

Console.WriteLine("SendCallback returning");
}

static void Main(string[] args)
{
// can be any server that accepts incoming TCP
// connections
const string TEST_HOST = "localhost";
const int TEST_PORT = 80;

ManualResetEvent send_done =
new ManualResetEvent(false);
try
{
SafeSocket socket = new SafeSocket(
new Socket(
AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp
)
);
try
{
socket.Connect(TEST_HOST, TEST_PORT);

List<ArraySegment<byte>> buffer_list =
new List<ArraySegment<byte>>();
ArraySegment<byte> segment =
new ArraySegment<byte>(
new byte[] { 0 }
);
buffer_list.Add(segment);

socket.BeginSend(
buffer_list,
SocketFlags.None,
SendCallback,
new SendCallBackArgs(
send_done, socket
)
);

// simulate timing that can cause
// callback and Close to race
Thread.Sleep(250);
}
finally
{
Console.WriteLine("doing work...");

// try to cancel send (or receive or
// whatever asynchronous callback is
// running) because it's time to
// shutdown
Console.WriteLine(
"shutting down subsystem..."
);
socket.Dispose();
}
}
finally
{
send_done.Close();
Console.WriteLine("Close called on event");
}

// simulate timing that can allow callback to
// proceed (and error)
Thread.Sleep(2000);

Console.WriteLine("end of test");
}

private class SendCallBackArgs
{
public SendCallBackArgs(
ManualResetEvent evt,
SafeSocket socket
)
{
this.evt = evt;
this.socket = socket;
}

private readonly ManualResetEvent evt;
public ManualResetEvent Event
{ get { return evt; } }

private readonly SafeSocket socket;
public SafeSocket Socket
{ get { return socket; } }
}
}


// a shutdown mechanism to prevent asynchronous
// callbacks from potentially accessing resources that
// have already been disposed

// for instance, a callback associated with
// System.Threading.Timer may still run after its
// parameterless Dispose has been called, and the same
// is true of the BeginSend method of the Socket class

class AsyncUsage
{
private bool shutting_down = false;
private bool shut_down = false;
private int count = 1;
private ManualResetEvent unused = null;

public AsyncUsage()
{
unused = new ManualResetEvent(false);
}

// to be called at the beginning of some
// asynchronous callback; returns true if usage is
// acquired
public bool Acquire()
{
// attempt to reserve usage
Interlocked.Increment(ref count);

// check if further asynchronous operations
// are prohibited;
// this must occur _after_ the increment
bool acquired = !shutting_down;
if(!acquired)
{
Release();
}

return acquired;
}

// to be called at the end of some asynchronous
// callback
public void Release()
{
int new_count = Interlocked.Decrement(
ref count
);
if(new_count == 0 && !shut_down)
{
shut_down = true;
unused.Set();
}
}

// to be called before any resources used by
// asynchronous callback(s) are disposed
public void FinalWaitRelease()
{
if(!shutting_down)
{
// prevent any new asynchronous operations
// from beginning; this must occur
// _before_ the Release
shutting_down = true;

// wait for all current asynchronous
// operations to finish
Release();
unused.WaitOne();

unused.Close();
unused = null;
}
}
}


// a socket wrapper that ensures its callback(s) will
// do nothing after a successful return from Dispose()
public class SafeSocket : IDisposable
{
private AsyncUsage async_usage = null;
private Socket socket = null;
private AsyncCallback
send_callback,
receive_callback;

private bool disposed = false;

public SafeSocket(Socket a_socket)
{
async_usage = new AsyncUsage();
socket = a_socket;
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
if(!this.disposed)
{
if(disposing)
{
// clean up managed resources

async_usage.FinalWaitRelease();

if(socket != null)
{
try
{
socket.Shutdown(
SocketShutdown.Both
);
}
finally
{
socket.Close();
socket = null;
}
}
}

// clean up unmanaged resources (if there
// are any)

disposed = true;
}
}

~SafeSocket()
{
Dispose(false);
}

public void Connect(string host, int port)
{
socket.Connect(host, port);
}

public IAsyncResult BeginSend(
IList<ArraySegment<byte>> buffers,
SocketFlags socketFlags,
AsyncCallback callback,
Object state
)
{
send_callback = callback;
return socket.BeginSend(
buffers,
socketFlags,
SafeSendCallback,
state
);
}

public int EndSend(IAsyncResult asyncResult)
{
return socket.EndSend(asyncResult);
}

public IAsyncResult BeginReceive(
IList<ArraySegment<byte>> buffers,
SocketFlags socketFlags,
AsyncCallback callback,
Object state
)
{
receive_callback = callback;
return socket.BeginReceive(
buffers,
socketFlags,
SafeReceiveCallback,
state
);
}

public int EndReceive(IAsyncResult asyncResult)
{
return socket.EndReceive(asyncResult);
}

private void SafeSendCallback(IAsyncResult ar)
{
if(async_usage.Acquire())
{
try
{
send_callback(ar);
}
finally
{
async_usage.Release();
}
}
}

private void SafeReceiveCallback(IAsyncResult ar)
{
if(async_usage.Acquire())
{
try
{
receive_callback(ar);
}
finally
{
async_usage.Release();
}
}
}
}
}


Output:

SendCallback entered
doing work...
shutting down subsystem...
SendCallback returning
Close called on event
end of test


*** Sent via Developersdex http://www.developersdex.com ***
.



Relevant Pages

  • Re: Can someone help me
    ... // contents from the server to the client public class AsynchNetworkFileServer {class ClientHandler {// constructor public ClientHandler(Socket socketForClient) {// initialize member variable socket = socketForClient; // initialize buffer to hold ... // contents of file buffer = new byte; // create the network stream networkStream = new NetworkStream; // set the file callback for reading ...
    (microsoft.public.dotnet.languages.csharp)
  • Re: Callback from native C++ to managed C++
    ... // Call into managed code using the callback pointer passing an int ... // private inner non GC class used as thunk to callback into managed code ... void CallNative() ...
    (microsoft.public.dotnet.framework.interop)
  • Re: UINT16 error
    ... // private inner non GC class used as thunk to callback into managed code ... static void __stdcall CallbackProc ... After the native "Process" is done, the bridge goes out of scope, ...
    (microsoft.public.dotnet.languages.vc)
  • Re: Applet Hangs when submitting data to servlet
    ... to put a time limit on my socket connections and socket reads. ... public void setTimeoutthrows UnknownHostException, ... on our web server. ... private JButton theSubmitButton_, theClearButton_; ...
    (comp.lang.java.programmer)
  • Re: Emulate AddressOf operator
    ... > any of it methods as a callback routine (in other words, ... Private Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ... Public Sub TimerProc() ... Public Property Let Interval ...
    (microsoft.public.vb.winapi)

Loading