2009-08-05 13 views
5

http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx.net asincrona presa timeout di controllo thread-sicurezza

A partire dal sopra MSDN esempio sto cercando di scrivere un assegno timeout che chiuderà i socket client inattivi e liberare risorse.

Questo è quello che ho trovato. Ma non sono sicuro che sia completamente sicuro per i thread e se esiste un modo migliore per farlo. Spero che qualcuno possa dare qualche consiglio.

void IO_Completed(object sender, SocketAsyncEventArgs e) 
{ 
    if (e.SocketError != SocketError.Success) 
    { 
     CloseClientSocket(e); 
     return; 
    } 

    if (1 < Interlocked.CompareExchange(ref token.Status, 1, 0)) 
     return; 

    switch (e.LastOperation) 
    { 
     case SocketAsyncOperation.Receive: 
      ProcessReceive(e); 
      break; 
     ... 
    } 

    token.LastActive = Environment.TickCount; 
    Interlocked.CompareExchange(ref token.Status, 0, 1); 
} 

void ProcessReceive(SocketAsyncEventArgs e) 
{ 
    AsyncUserToken token = (AsyncUserToken)e.UserToken; 
    if (e.BytesTransferred > 0) 
     if (!token.Socket.SendAsync(e)) 
      ProcessSend(e); 
    else 
     CloseClientSocket(e); 
} 

void ProcessSend(SocketAsyncEventArgs e) 
{ 
    AsyncUserToken token = (AsyncUserToken)e.UserToken; 
    if (!token.Socket.ReceiveAsync(e)) 
     ProcessReceive(e); 
} 

TimeoutCheck verrà eseguito una volta ogni 20 secondi. allReadWriteArgs è un array con tutto SocketAsyncEventArgs. Dopo la chiusura, il socket IO_Completed verrà richiamato con SocketError.OperationAborted.

void TimeoutCheck(object state) 
{ 
    AsyncUserToken token; 
    int timeout = Environment.TickCount - 20000; 
    for (int i = 0; i < allReadWriteArgs.Length; i++) 
    { 
     token = (AsyncUserToken)allReadWriteArgs[i].UserToken; 
     if (token.LastActive < timeout) 
      if (0 == Interlocked.CompareExchange(ref token.Status, 2, 0)) 
       Interlocked.Exchange(ref token.Socket, null).Close(); 
    } 
} 


void CloseClientSocket(SocketAsyncEventArgs e) 
{ 
    AsyncUserToken token = e.UserToken as AsyncUserToken; 

    if (token.Socket != null) 
    { 
     try 
     { 
      token.Socket.Shutdown(SocketShutdown.Both); 
     } 
     catch (SocketException) { } 
     token.Socket.Close(); 
    } 

    token.Status = 2; 
    bufferManager.FreeBuffer(e); 
    readWritePool.Push(e); 
    ... 
} 
+0

Questo è un pò vecchio, ma per chi legge che in futuro: non utilizzare 'Environment.TickCount' per qualsiasi calcolo nell'applicazione. È rappresentato come 'Int32' che si esaurirà in 24,9 giorni e passerà ad un numero intero negativo e alla fine restituirà 0. Utilizzare invece' DateTime.UtcNow.Ticks'. –

risposta

1

Il codice sembra buono. Si può fare qualcosa di simile, così:

void _connectionActivityCheck_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
{ 
    _connectionActivityCheck.Stop(); 

    try 
    { 
     List<Guid> connectionsToRemove = new List<Guid>(); 

     lock (_connections.SyncRoot) 
     { 
      IncomingConnection conn; 

      foreach (DictionaryEntry item in _connections) 
      { 
       conn = (IncomingConnection)item.Value; 

       if (conn.LastIncomingActivity.HasValue && 
        DateTime.Now.Subtract(conn.LastIncomingActivity.Value).TotalSeconds > MaximumInactivitySeconds) 
         connectionsToRemove.Add(conn.ConnectionId); 
      } 
     } 

     if (connectionsToRemove.Count > 0) 
     { 
      int itemsToRemove = connectionsToRemove.Count; 

      foreach (Guid item in connectionsToRemove) 
      { 
       RemoveConnection(item); 
      } 

      Context.Current.Logger.LogInfo(_loggerName, 
       string.Format("{0} connections were closed due to incoming traffic inactivity", itemsToRemove)); 
     } 
    } 
    catch (Exception ex) 
    { 
     Context.Current.Logger.LogFatal(_loggerName, "An error ocurred while checking incoming traffic.", ex); 
    } 
    finally 
    { 
     _connectionActivityCheck.Start(); 
    } 
} 



private void RemoveConnection(Guid connectionId) 
{ 
    lock (_connections.SyncRoot) 
    { 
     try 
     { 
      IncomingConnection conn = _connections[connectionId] as IncomingConnection; 

      if (conn != null) 
      { 
       try 
       { 
        conn.Dispose(); 
       } 
       catch { } 

       _connections.Remove(connectionId); 
      } 
     } 
     catch { } 
    } 
} 
Problemi correlati