Thanks everyone for their help.

I've gone with Heath's suggestion of using the async calls.  I was hoping
I wouldn't have to do that, as it has ended up with more convoluted
threaded calls but it seems like the most complete solution.

Thx
Phil

P.s in case anyone has a similar need, I've posted the code below:


public class TcpConnectCall
{
 #region member variables
 Socket m_socket;
 bool m_isConnected = false;
 bool m_cancelConnect = false;
 System.Exception m_connectFailureException = null;
 System.Object m_connectSyncRoot = new object();
 System.Object m_disposingSyncRoot = new object();
 System.Threading.AutoResetEvent m_connectCallbackCompleted = new
AutoResetEvent(false);
 #endregion

 #region public static methods
 /// <summary>
 /// Attempts to make a socket connection for a limited time
specified by timeoutMs.
 /// Performs attempt using async socket calls (BeginConnect etc.).
 /// If the connection does not succeed during this time then an
exception will be
 /// thrown and if the connect call later succeeds, the socket will
be immediately closed
 ///
 /// Note: it is *VERY IMPORTANT* that clients do NOT attempt to
make further use of
 ///       the socket instance passed to this call IF an exception
is generated by
 ///       this method.  The reason being that the connect call may
still be queued
 ///       AND when the queued call is executed the socket instance
will automatically
 ///       be CLOSED.  Further attempts to make a connection to the
same end-point
 ///       MUST be made on a different socket instance.
 /// </summary>
 /// <param name="socket"></param>
 /// <param name="endPoint"></param>
 /// <param name="timeoutMs"></param>
 public static void Connect(Socket socket, EndPoint endPoint, int
timeoutMs)
 {
  TcpConnectCall connectCall = new TcpConnectCall(socket);

  try
  {
   // create callback delegate to TcpConnectCall
instance's ConnectedCallback method
   AsyncCallback connectedCallback = new AsyncCallback
(connectCall.ConnectedCallback);

   // initiate connection attempt and pass the
TcpConnectCall instance as the
   // state variable (necessary? I don't think I use
the state var - could pass null)
   IAsyncResult result = socket.BeginConnect
(endPoint, connectedCallback, connectCall);

   // wait for timeout for connect
   if(result.AsyncWaitHandle.WaitOne(timeoutMs,
false) == false)
   {
    // failed to connect - cancel connection
call attempt
    connectCall.CancelConnect();

    // throw exception
    throw new Exception("Timed out on
connection attempt");
   }
   else
   {
    // we didn't timeout on the connection.
    // However, a socket exception could still
have occurred

    // This means that we MUST wait until the
connect callback has completed
    // as it is possible for this code to be
executed prior to the callback
    // being called and having a chance to
check for and store any exception
    // information.  Indeed this behavior was
exhibited during testing.
    // This is because the waithandle get
signalled prior to completion of
    // the connect callback
    if
(connectCall.m_connectCallbackCompleted.WaitOne(15000, false) == false)
    {
     // we timed out waiting for the
connect callback to complete!!!!
     // Really shouldn't happen as the
connect callback should be executed
     // pretty much instantly that the
async wait handle is raised

     throw new Exception(
      "Timed out after
connection made, waiting on connect callback method to finish");
    }

    System.Exception connectException =
connectCall.m_connectFailureException;
    if(connectException != null)
    {
     // a socket exception DID occur -
throw an exception with the
     // recorded exception stored in
the exception's InnerException property
     throw new Exception("Exception
occurred during connect attempt: " +
      connectException.Message,
connectException);
    }
   }
  }
  finally
  {
   connectCall.Dispose();
  }
 }
 #endregion

 #region constructors - hidden to ensure static access
 private TcpConnectCall(Socket socket)
 {
  // record socket on which connect attempt will be made
  m_socket = socket;
 }
 #endregion

 #region private methods
 private void Dispose()
 {
  lock(m_disposingSyncRoot)
  {
   m_connectCallbackCompleted.Close();
   m_connectCallbackCompleted = null;
  }
 }

 private void CancelConnect()
 {
  lock(m_connectSyncRoot)
  {
   // check if we have already connected, i.e. has
the ConnectedCallback method
   // already been called - if we have then we need
to close the connection
   // otherwise the callback method will handle
closing the connection when
   // it gets called
   if(m_isConnected)
   {
    m_socket.Close();
    m_isConnected = false;
   }

   m_cancelConnect = true;
  }
 }

 private void ConnectedCallback(IAsyncResult result)
 {
  lock(m_connectSyncRoot)
  {
   try
   {
    // make call to end connect - will throw a
SocketException
    // if there was a failure to connect
    m_socket.EndConnect(result);

    // if we get here connected successfully
    m_isConnected = true;
   }
   catch(Exception exception)
   {
    // failed to connect, record exception
details but don't
    // allow propogation of exception
    m_connectFailureException = exception;
   }

   // now check if we have been instructed to cancel
the connection -
   // usually because the client which requested the
tcp connection
   // attempt has timed out the connect attempt - in
which case the
   // queued connection attempt needs cancelling,
i.e. close the socket
   if(m_cancelConnect)
   {
    m_socket.Close();
    m_isConnected = false;
   }
  }

  // ok finished method - signal this
  // first though we need to check if we still have an event
object on which
  // to signal (we may be operating on a disposed object, in
which case the
  // event object will be equal to null)
  // Also, have to lock against dispose being called
concurrently
  lock(m_disposingSyncRoot)
  {
   if(m_connectCallbackCompleted != null)
m_connectCallbackCompleted.Set();
  }
 }
 #endregion
}

===================================
This list is hosted by DevelopMentor�  http://www.develop.com
Some .NET courses you may be interested in:

NEW! Guerrilla ASP.NET, 17 May 2004, in Los Angeles
http://www.develop.com/courses/gaspdotnetls

View archives and manage your subscription(s) at http://discuss.develop.com

Reply via email to