2009-08-05 15 views
5

http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx.net asynchroniczny gniazdo Timeout check wątku bezpieczeństwa

Wychodząc z powyższego przykładu msdn próbuję wypisać czek limitu czasu, który zamknie nieaktywnych gniazda klienckie i wolnych zasobów.

Oto, co wymyśliłem. Ale nie jestem pewien, czy jest całkowicie bezpieczny dla wątków i czy jest lepszy sposób na zrobienie tego. Mam nadzieję, że ktoś może udzielić kilku rad.

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

Kontrola TimeoutCheck zostanie wykonana co 20 sekund. allReadWriteArgs to tablica z wszystkimi SocketAsyncEventArgs. Po zamknięciu gniazda IO_Completed zostanie wywołany z 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

To jest trochę stary, ale ktokolwiek przeczytać, że w przyszłości: nie używać 'Environment.TickCount' dla wszystkich obliczeń w aplikacji. Jest reprezentowany jako 'Int32', który skończy się w 24,9 dni i zapętla się do ujemnej liczby całkowitej i ostatecznie zwraca 0. Zamiast tego użyj' DateTime.UtcNow.Ticks'. –

Odpowiedz

1

Twój kod wygląda dobrze. Można zrobić coś takiego, jak:

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 { } 
    } 
} 
Powiązane problemy