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