2009-08-05 10 views
5

http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx.net asíncrono toma tiempo de espera de comprobación de hilo de seguridad

A partir de lo anterior ejemplo de MSDN Estoy tratando de escribir un cheque de tiempo de espera que se cerrará sockets de cliente inactivas y liberar recursos.

Esto es lo que se me ocurrió. Pero no estoy seguro si es completamente seguro para subprocesos y si hay una mejor manera de hacerlo. Espero que alguien pueda dar algunos consejos.

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 se ejecutará una vez cada 20 segundos. allReadWriteArgs es una matriz con todos los SocketAsyncEventArgs. Después de cerrar el socket, IO_Completed se invocará 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

Este es un poco viejo, pero para el que lee que en el futuro: no utilice 'Environment.TickCount' para cualquier cálculo en su aplicación. Se representa como 'Int32', que se agotará en 24.9 días y se convertirá en un entero negativo y eventualmente devolverá 0. Use' DateTime.UtcNow.Ticks' en su lugar. –

Respuesta

1

Su código se ve bien. Se puede hacer algo como esto, así:

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 { } 
    } 
} 
Cuestiones relacionadas