2011-11-17 64 views
7

當運行此代碼:爲什麼ConnectTimeout在這種情況下被忽略?

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)); 
    } 
} 

我得到這樣的結果:

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 

我預計ConnectTimeout屬性有效果。那麼爲什麼在這種情況下ConnectTimeout屬性被忽略? (我也很好奇別人看到的時間)。

更新:我注意到,下面的多餘行將時間跨度縮短到26秒?

[email protected]"9.9.9.9"; 
+0

我看到一個smilar問題http://stackoverflow.com/questions/3011394/超時不工作的SQL連接,即使原始提問者評論說答案無效,答案也被接受。O_o –

+1

將停止時間放在catch語句之後,以查看代碼是否在大約1秒後捕獲異常。此外,請查看以下鏈接:http://improve.dk/archive/2008/03/10/controlling-sqlconnection-timeouts.aspx – HardCode

+0

即使停止時間在catch塊中,代碼也需要大約47秒。 –

回答

7

編輯:奇怪,因爲這看起來,我把一個破發點深的反編譯代碼和超時值設置爲1用 - >VALID < - 服務器的名字,我讓我的斷點坐在那裏,然後繼續,它給了超時過期異常如預期因此,似乎ConnectTimeout只適用於它能夠解析服務器並等待連接。 It DOES NOT影響解決服務器連接到。我認爲目前所經歷的是服務器解析,而不是「連接」的實際行爲。至少這是我目前的假設。

我用反射器來看看蓋子下面發生了什麼。也許有人從微軟可以幫助我們在這裏,因爲我也發現ConnectTimeout似乎沒有影響初始連接。

反正在內部建立連接,下面的方法被調用,在這個序列中,我認爲:

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; 
    } 

然後:

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); 
    } 

然後:

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); 
     } 
    } 

然後默認(無故障轉移夥伴):

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; 
    } 

然後:

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; 
     } 
     } 
    } 

我不知道這一切是如何拼在一起,但infiniteTimeout似乎是真實的。

不知道這是否有助於任何,但我想這是值得挖掘通過

enter image description here

enter image description here

+0

幹得好。我認爲你是對的,並且要帶走的教訓是,如果你打電話給Open(),你不能保證在連接超時秒後得到響應(使屬性幾乎無用)。 @HardCode指出了一篇由Mark S. Rasmussen撰寫的文章http://improve.dk/archive/2008/03/10/controlling-sqlconnection-timeouts.aspx,它有一個DIY超時功能,可以解決這個問題。 –

+0

順便說一句,我跑了一個BID跟蹤(這不容易設置!)並且注意到它在放棄之前嘗試了TCP/IP和命名管道。我不確定這是如何適合這個故事...... –

+0

是的,它令人困惑,但我想連接超時不能強制執行,直到它能夠解析服務器。服務器解析後,連接時間開始計時。微軟應該讓這個更清楚:)或糾正我們,如果我們錯了。 – kd7

相關問題