.Net Sockets -> бросить System.InvalidOperationException - PullRequest
1 голос
/ 26 октября 2009

У меня уже давно есть эта проблема, и я так и не смог ее решить. Эта проблема возникает только при использовании методов SendAsync / ReceiveAsync, а не методов сокетов Begin / EndSend для асинхронных операций.

Если у вас довольно сложная библиотека сокетов tcp и вы хотели заменить методы BeginSend на SendAsync, но из-за проблемы, с которой я столкнулся, мне всегда приходилось ее откладывать. Мой сокет-сервер обрабатывает тяжелые стрессовые сценарии с более чем 1000 подключенными клиентами, которые постоянно работают со скоростью более 100 Мбит / с, и я хотел бы использовать метод SendAsync, чтобы у меня не было накладных расходов на распределение IAsyncResult.

В любом случае, что происходит, все работает нормально, пока я просто отправляю / получаю данные, однако в сценариях с высокой нагрузкой, когда сервер пытается отключить / выключить клиент, я иногда получаю следующее исключение:

    System.InvalidOperationException was unhandled
  Message=Cannot apply a context that has been marshaled across AppDomains, that was not acquired through a Capture operation or that has already been the argument to a Set call.
  Source=mscorlib
  StackTrace:
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSuccess(SocketError socketError, Int32 bytesTransferred, SocketFlags flags)
       at System.Net.Sockets.SocketAsyncEventArgs.CompletionPortCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
       at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
  InnerException: 

Я не могу поймать это исключение в любом месте, так как оно, похоже, происходит в .NET Framework, и я ничего не могу поделать, если мой сервер рухнет.

Поскольку это происходит только тогда, когда я вызываю свою процедуру завершения работы, я предполагаю, что это как-то связано с вызовом Shutdown для сокета, пока у него все еще есть процедура чтения / записи. Однако я также пытался отложить отключение вызова до тех пор, пока не будут возвращены все вызовы «чтение / запись» Send / ReceiveAsync и отключение вызова после этого, но это тоже не помогло.

Вот как я пытаюсь отключить сокеты:

        private void InternalDisconnect(SocketError socketError)
    {
        lock (shutdownLock)
        {
            if (isShutdown)
                return;

            isShutdown = true;
        }

        allowSend = false;
        SocketError = socketError;

        ThreadPool.QueueUserWorkItem(delegate
                                         {
                                             lock (padLock)
                                             {
                                                 try
                                                 {
                                                     if (TcpSocketStatus == TcpSocketStatus.Disconnected)
                                                         return;

                                                     TcpSocketStatus = TcpSocketStatus.Disconnecting;

                                                     if (asyncSendArgs != null)
                                                     {
                                                         asyncSendArgs.Completed -= SendCompleted;
                                                         asyncSendArgs.SetBuffer(null, 0, 0);
                                                         asyncSendArgs.Dispose();
                                                     }

                                                     if (asyncReceiveArgs != null)
                                                     {
                                                         asyncReceiveArgs.Completed -= ReceiveCompleted;
                                                         asyncReceiveArgs.SetBuffer(null, 0, 0);
                                                         asyncReceiveArgs.Dispose();
                                                     }

                                                     try
                                                     {
                                                         bufferedSender.Clear();

                                                         Socket.Shutdown(SocketShutdown.Both);

                                                         if (Socket.Connected)
                                                         {
                                                             Socket.Disconnect(true);
                                                         }
                                                     }
                                                     catch
                                                     {
                                                     }

                                                     try
                                                     {
                                                         Socket.Close();
                                                     }
                                                     catch
                                                     {
                                                     }

                                                     TcpSocketStatus = TcpSocketStatus.Disconnected;

                                                     if (socketError != SocketError.Success)
                                                     {
                                                         if (log.IsDebugEnabled)
                                                             log.Debug("SocketDisconnected\tSocketError:{0}", socketError);
                                                     }
                                                     else
                                                     {
                                                         if (log.IsDebugEnabled)
                                                             log.Debug("SocketDisconnected");
                                                     }

                                                     DisconnectTime = DateTime.UtcNow;

                                                     if (TcpSocketDisconnected != null)
                                                         TcpSocketDisconnected(this);
                                                 }
                                                 catch (Exception ex)
                                                 {
                                                     log.ErrorException("InternalDisconnect", ex);
                                                 }
                                             }
                                         });
    }

1 Ответ

2 голосов
/ 26 октября 2009

Хорошо, по некоторым причинам это всегда помогает мне задать вопрос, потому что я склонен выяснять вещи всегда вскоре после: /

Кажется, что asyncSendArgs.SetBuffer (null, 0, 0); виновник Несмотря на то, что другой фрагмент кода должен убедиться, что перед вызовом InternalDisconnect не осталось невыполненных асинхронных операций, похоже, что все еще есть, и изменение буфера во время его использования определенно вызовет проблему. До сих пор я проводил тяжелые стресс-тесты в течение 15 минут, и все выглядит нормально (раньше я всегда получал это исключение в течение одной минуты). Я позволю этому работать еще некоторое время и надеюсь, что это сделало это.

...