2011-11-17 17 views
7

Quando si esegue questo codice:Perché in questo caso ConnectTimeout viene ignorato?

static void Main(string[] args) 
{ 
    SqlConnectionStringBuilder csb = new SqlConnectionStringBuilder(); 
    csb.DataSource = @"8.8.8.8"; // some inaccessible ip address 
    csb.InitialCatalog = "Tempdb"; 
    csb.IntegratedSecurity = true; 
    csb.ConnectTimeout = 1; 
    DateTime start = DateTime.Now; 
    try 
    { 
     new SqlConnection(csb.ToString()).Open(); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex.Message); 
    } 
    finally 
    { 
     Console.Write(string.Format("{0} seconds", DateTime.Now.Subtract(start).TotalSeconds)); 
    } 
} 

ottengo questo risultato:

A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server) 
47.6097605 seconds 

mi aspettavo la proprietà ConnectTimeout per avere un effetto. Quindi, perché la proprietà ConnectTimeout è ignorata in questo caso? (Sono anche curioso di sapere che ore vedono gli altri).

Aggiornamento: Ho notato che la seguente linea aggiuntiva accorcia l'intervallo di tempo a 26 secondi ???

[email protected]"9.9.9.9"; 
+0

ho visto un problema smilar http://stackoverflow.com/questions/3011394/ timeout-not-working-in-sql-connection in cui è stata accettata una risposta anche se il richiedente originale ha commentato che la risposta non ha funzionato.O_o –

+1

Metti il ​​tempo di arresto subito dopo l'istruzione catch per vedere se il codice sta recuperando l'eccezione dopo circa 1 secondo. Inoltre, consulta questo link: http://improve.dk/archive/2008/03/10/controlling-sqlconnection-timeouts.aspx – HardCode

+0

Il codice impiega circa 47 secondi anche se il tempo di arresto è nel blocco catch. –

risposta

7

EDIT: Per quanto strano possa sembrare, ho messo un punto di rottura profonda nel codice decompilato e impostare il timeout a 1 con un - <>VALIDO - nome del server, ho lasciato il mio breakpoint si è seduto lì e poi è continuato, e ha dato l'eccezione scaduta come previsto Quindi sembra che ConnectTimeout si applica SOLO quando è in grado di risolvere il server e attende una connessione. È NON ha effetto che risolve il server per la connessione a. Penso che il tempo che si sta provando sia per la risoluzione del server e non per l'effettivo atto di "connessione". Almeno questa è la mia ipotesi attuale.

Ho usato il riflettore per vedere cosa stava succedendo sotto le coperte. Forse qualcuno da Microsoft può aiutarci qui, perché ho anche scoperto che ConnectTimeout sembra non avere alcun effetto sulla connessione iniziale.

Comunque internamente per stabilire la connessione, i seguenti metodi vengono chiamati, in questa sequenza penso:

internal DbConnectionInternal CreatePooledConnection(DbConnection owningConnection, DbConnectionPool pool, DbConnectionOptions options) 
    { 
     DbConnectionPoolGroupProviderInfo providerInfo = pool.PoolGroup.ProviderInfo; 
     DbConnectionInternal internal2 = this.CreateConnection(options, providerInfo, pool, owningConnection); 
     if (internal2 != null) 
     { 
     this.PerformanceCounters.HardConnectsPerSecond.Increment(); 
     internal2.MakePooledConnection(pool); 
     } 
     Bid.Trace("<prov.DbConnectionFactory.CreatePooledConnection|RES|CPOOL> %d#, Pooled database connection created.\n", this.ObjectID); 
     return internal2; 
    } 

E poi:

protected override DbConnectionInternal CreateConnection(DbConnectionOptions options, object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection) 
    { 
     string instanceName; 
     SqlConnectionString str = (SqlConnectionString) options; 
     if (str.ContextConnection) 
     { 
     return this.GetContextConnection(str, poolGroupProviderInfo, owningConnection); 
     } 
     bool redirectedUserInstance = false; 
     DbConnectionPoolIdentity current = null; 
     if (str.IntegratedSecurity) 
     { 
     if (pool != null) 
     { 
      current = pool.Identity; 
     } 
     else 
     { 
      current = DbConnectionPoolIdentity.GetCurrent(); 
     } 
     } 
     if (!str.UserInstance) 
     { 
     goto Label_00F1; 
     } 
     redirectedUserInstance = true; 
     if ((pool == null) || ((pool != null) && (pool.Count <= 0))) 
     { 
     using (SqlInternalConnectionTds tds = null) 
     { 
      SqlConnectionString connectionOptions = new SqlConnectionString(str, str.DataSource, true, false); 
      tds = new SqlInternalConnectionTds(current, connectionOptions, null, "", null, false); 
      instanceName = tds.InstanceName; 
      if (!instanceName.StartsWith(@"\\.\", StringComparison.Ordinal)) 
      { 
      throw SQL.NonLocalSSEInstance(); 
      } 
      if (pool != null) 
      { 
      SqlConnectionPoolProviderInfo info2 = (SqlConnectionPoolProviderInfo) pool.ProviderInfo; 
      info2.InstanceName = instanceName; 
      } 
      goto Label_00DB; 
     } 
     } 
     SqlConnectionPoolProviderInfo providerInfo = (SqlConnectionPoolProviderInfo) pool.ProviderInfo; 
     instanceName = providerInfo.InstanceName; 
    Label_00DB: 
     str = new SqlConnectionString(str, instanceName, false, null); 
     poolGroupProviderInfo = null; 
    Label_00F1: 
     return new SqlInternalConnectionTds(current, str, poolGroupProviderInfo, "", (SqlConnection) owningConnection, redirectedUserInstance); 
    } 

e poi:

internal SqlInternalConnectionTds(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, object providerInfo, string newPassword, SqlConnection owningObject, bool redirectedUserInstance) : base(connectionOptions) 
    { 
     this._instanceName = string.Empty; 
     if (connectionOptions.UserInstance && InOutOfProcHelper.InProc) 
     { 
     throw SQL.UserInstanceNotAvailableInProc(); 
     } 
     this._identity = identity; 
     this._poolGroupProviderInfo = (SqlConnectionPoolGroupProviderInfo) providerInfo; 
     this._fResetConnection = connectionOptions.ConnectionReset; 
     if (this._fResetConnection) 
     { 
     this._originalDatabase = connectionOptions.InitialCatalog; 
     this._originalLanguage = connectionOptions.CurrentLanguage; 
     } 
     RuntimeHelpers.PrepareConstrainedRegions(); 
     try 
     { 
     TimeoutTimer timeout = TimeoutTimer.StartSecondsTimeout(connectionOptions.ConnectTimeout); 
     this.OpenLoginEnlist(owningObject, timeout, connectionOptions, newPassword, redirectedUserInstance); 
     } 
     catch (OutOfMemoryException) 
     { 
     base.DoomThisConnection(); 
     throw; 
     } 
     catch (StackOverflowException) 
     { 
     base.DoomThisConnection(); 
     throw; 
     } 
     catch (ThreadAbortException) 
     { 
     base.DoomThisConnection(); 
     throw; 
     } 
     if (Bid.AdvancedOn) 
     { 
     Bid.Trace("<sc.SqlInternalConnectionTds.ctor|ADV> %d#, constructed new TDS internal connection\n", base.ObjectID); 
     } 
    } 

e quindi, per impostazione predefinita (senza partner Failover):

private void LoginNoFailover(ServerInfo serverInfo, string newPassword, bool redirectedUserInstance, SqlConnection owningObject, SqlConnectionString connectionOptions, TimeoutTimer timeout) 
    { 
     if (Bid.AdvancedOn) 
     { 
     Bid.Trace("<sc.SqlInternalConnectionTds.LoginNoFailover|ADV> %d#, host=%ls\n", base.ObjectID, serverInfo.UserServerName); 
     } 
     int num = 100; 
     this.ResolveExtendedServerName(serverInfo, !redirectedUserInstance, owningObject); 
     while (true) 
     { 
     if (this._parser != null) 
     { 
      this._parser.Disconnect(); 
     } 
     this._parser = new TdsParser(base.ConnectionOptions.MARS, base.ConnectionOptions.Asynchronous); 
     try 
     { 
      this.AttemptOneLogin(serverInfo, newPassword, true, timeout, owningObject); 
      break; 
     } 
     catch (SqlException exception) 
     { 
      if (((this._parser == null) || (this._parser.State != TdsParserState.Closed)) || (this.IsDoNotRetryConnectError(exception.Number) || timeout.IsExpired)) 
      { 
      throw; 
      } 
      if (timeout.MillisecondsRemaining <= num) 
      { 
      throw; 
      } 
     } 
     if (this.ServerProvidedFailOverPartner != null) 
     { 
      this.LoginWithFailover(true, serverInfo, this.ServerProvidedFailOverPartner, newPassword, redirectedUserInstance, owningObject, connectionOptions, timeout); 
      return; 
     } 
     if (Bid.AdvancedOn) 
     { 
      Bid.Trace("<sc.SqlInternalConnectionTds.LoginNoFailover|ADV> %d#, sleeping %d{milisec}\n", base.ObjectID, num); 
     } 
     Thread.Sleep(num); 
     num = (num < 500) ? (num * 2) : 0x3e8; 
     } 
     if (this.PoolGroupProviderInfo != null) 
     { 
     this.PoolGroupProviderInfo.FailoverCheck(this, false, connectionOptions, this.ServerProvidedFailOverPartner); 
     } 
     base.CurrentDataSource = serverInfo.UserServerName; 
    } 

e poi:

internal void Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, bool ignoreSniOpenTimeout, long timerExpire, bool encrypt, bool trustServerCert, bool integratedSecurity) 
    { 
     if (this._state == TdsParserState.Closed) 
     { 
     this._connHandler = connHandler; 
     if (SNILoadHandle.SingletonInstance.SNIStatus != 0) 
     { 
      this.Errors.Add(this.ProcessSNIError(this._physicalStateObj)); 
      this._physicalStateObj.Dispose(); 
      this.ThrowExceptionAndWarning(); 
     } 
     if (integratedSecurity) 
     { 
      this.LoadSSPILibrary(); 
      this._sniServerUserName = new byte[s_maxSSPILength]; 
      Bid.Trace("<sc.TdsParser.Connect|SEC> SSPI authentication\n"); 
     } 
     else 
     { 
      Bid.Trace("<sc.TdsParser.Connect|SEC> SQL authentication\n"); 
     } 
     byte[] instanceName = null; 
     this._physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, integratedSecurity, this._sniServerUserName, false, this._fAsync); 
     if (this._physicalStateObj.Status != 0) 
     { 
      this.Errors.Add(this.ProcessSNIError(this._physicalStateObj)); 
      this._physicalStateObj.Dispose(); 
      Bid.Trace("<sc.TdsParser.Connect|ERR|SEC> Login failure\n"); 
      this.ThrowExceptionAndWarning(); 
     } 
     this._server = serverInfo.ResolvedServerName; 
     if (connHandler.PoolGroupProviderInfo != null) 
     { 
      connHandler.PoolGroupProviderInfo.AliasCheck(serverInfo.ResolvedServerName); 
     } 
     this._state = TdsParserState.OpenNotLoggedIn; 
     this._physicalStateObj.SniContext = SniContext.Snix_PreLoginBeforeSuccessfullWrite; 
     this._physicalStateObj.TimeoutTime = timerExpire; 
     bool marsCapable = false; 
     this.SendPreLoginHandshake(instanceName, encrypt); 
     this._physicalStateObj.SniContext = SniContext.Snix_PreLogin; 
     switch (this.ConsumePreLoginHandshake(encrypt, trustServerCert, out marsCapable)) 
     { 
      case PreLoginHandshakeStatus.SphinxFailure: 
      this._fMARS = false; 
      this._physicalStateObj._sniPacket = null; 
      this._physicalStateObj.SniContext = SniContext.Snix_Connect; 
      this._physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, integratedSecurity, this._sniServerUserName, false, this._fAsync); 
      if (this._physicalStateObj.Status != 0) 
      { 
       this.Errors.Add(this.ProcessSNIError(this._physicalStateObj)); 
       Bid.Trace("<sc.TdsParser.Connect|ERR|SEC> Login failure\n"); 
       this.ThrowExceptionAndWarning(); 
      } 
      break; 

      case PreLoginHandshakeStatus.InstanceFailure: 
      this._physicalStateObj.Dispose(); 
      this._physicalStateObj.SniContext = SniContext.Snix_Connect; 
      this._physicalStateObj.CreatePhysicalSNIHandle(serverInfo.ExtendedServerName, ignoreSniOpenTimeout, timerExpire, out instanceName, integratedSecurity, this._sniServerUserName, true, this._fAsync); 
      if (this._physicalStateObj.Status != 0) 
      { 
       this.Errors.Add(this.ProcessSNIError(this._physicalStateObj)); 
       Bid.Trace("<sc.TdsParser.Connect|ERR|SEC> Login failure\n"); 
       this.ThrowExceptionAndWarning(); 
      } 
      this.SendPreLoginHandshake(instanceName, encrypt); 
      if (this.ConsumePreLoginHandshake(encrypt, trustServerCert, out marsCapable) == PreLoginHandshakeStatus.InstanceFailure) 
      { 
       Bid.Trace("<sc.TdsParser.Connect|ERR|SEC> Login failure\n"); 
       throw SQL.InstanceFailure(); 
      } 
      break; 
     } 
     if (this._fMARS && marsCapable) 
     { 
      this._sessionPool = new TdsParserSessionPool(this); 
     } 
     else 
     { 
      this._fMARS = false; 
     } 
     } 
    } 

io non sono sicuro di come questo tutti i pezzi insieme, ma infiniteTimeout sembra essere vero.

Non sono sicuro se questo aiuta qualsiasi, ma ho pensato che valeva la pena scavare attraverso

enter image description here

enter image description here

+0

Ben fatto. Penso che tu abbia ragione e la lezione da portare via è che se chiami Open(), non puoi avere la certezza di ottenere una risposta dopo il tempo di connessione in secondi (rendendo la proprietà quasi inutile). @HardCode ha indicato un articolo di Mark S. Rasmussen http://improve.dk/archive/2008/03/10/controlling-sqlconnection-timeouts.aspx che ha un timeout fai-da-te che funziona attorno a questo. –

+0

BTW, ho eseguito una traccia BID (che non è facile da configurare!) E ho notato che ha provato sia TCP/IP che Named Pipes prima di rinunciare. Non sono sicuro di come questo si inserisca nella storia ... –

+0

Sì, è un po 'confuso, ma immagino che il timeout della connessione non venga applicato fino a quando non sarà in grado di risolvere il server. Il tempo inizia a spuntare sulla durata della connessione una volta che il server è stato risolto. Microsoft dovrebbe probabilmente renderlo un po 'più chiaro :) o correggerci se abbiamo torto. – kd7

Problemi correlati