private void InternalConnectCallback(object sender, SocketAsyncEventArgs args)
{
Exception exception = null;
lock (_lockObject)
{
if (_state == State.Canceled)
{
// If Cancel was called before we got the lock, the Socket will be closed soon. We need to report
// OperationAborted (even though the connection actually completed), or the user will try to use a
// closed Socket.
exception = new SocketException((int)SocketError.OperationAborted);
}
else if (_state == State.ConnectAttempt)
{
if (args.SocketError == SocketError.Success)
{
if (RequiresUserConnectAttempt)
{
exception = AttemptUserConnection();
if (exception == null)
{
// Don't call the callback; we've started a connection attempt on the
// user's socket.
_state = State.UserConnectAttempt;
return;
}
}
// The connection attempt succeeded or the user connect attempt failed synchronously; go to the
// completed state. The callback will be called outside the lock.
_state = State.Completed;
}
else if (args.SocketError == SocketError.OperationAborted)
{
// The socket was closed while the connect was in progress. This can happen if the user
// closes the socket, and is equivalent to a call to CancelConnectAsync
exception = new SocketException((int)SocketError.OperationAborted);
_state = State.Canceled;
}
else
{
// Try again, if there are more IPAddresses to be had.
// If the underlying OS does not support multiple connect attempts
// on the same socket, dispose the socket used for the last attempt.
if (!SocketPal.SupportsMultipleConnectAttempts)
{
_lastAttemptSocket.Dispose();
}
// Keep track of this because it will be overwritten by AttemptConnection
SocketError currentFailure = args.SocketError;
Exception connectException = AttemptConnection();
if (connectException == null)
{
// don't call the callback, another connection attempt is successfully started
return;
}
else
{
SocketException socketException = connectException as SocketException;
if (socketException != null && socketException.SocketErrorCode == SocketError.NoData)
{
// If the error is NoData, that means there are no more IPAddresses to attempt
// a connection to. Return the last error from an actual connection instead.
exception = new SocketException((int)currentFailure);
}
else
{
exception = connectException;
}
_state = State.Completed;
}
}
}
else
{
if (_state != State.UserConnectAttempt)
{
NetEventSource.Fail(this, "Unexpected object state");
}
if (!RequiresUserConnectAttempt)
{
NetEventSource.Fail(this, "State.UserConnectAttempt without RequiresUserConnectAttempt");
}
if (args.SocketError == SocketError.Success)
{
_state = State.Completed;
}
else if (args.SocketError == SocketError.OperationAborted)
{
// The socket was closed while the connect was in progress. This can happen if the user
// closes the socket, and is equivalent to a call to CancelConnectAsync
exception = new SocketException((int)SocketError.OperationAborted);
_state = State.Canceled;
}
else
{
// The connect attempt on the user's socket failed. Return the corresponding error.
exception = new SocketException((int)args.SocketError);
_state = State.Completed;
}
}
}
if (exception == null)
{
Succeed();
}
else
{
AsyncFail(exception);
}
}