2010-10-17 4 views
12

Что касается метода C# и .NET System.Net.Sockets.Socket.AcceptAsync, то потребуется обработать возвращаемое значение «false», чтобы обрабатывать сразу доступное состояние SocketAsyncEventArgs от синхронно обработанного соединения. Microsoft предоставляет примеры (найденные на странице класса System.Net.Sockets.SocketAsyncEventArgs), что приведет к переполнению стека, если существует большое количество ожидающих подключения соединений, которые могут быть использованы в любой системе, которая реализует свою модель обработки.Переполнение стека при использовании System.Net.Sockets.Socket.AcceptAsync модели

Другие идеи для решения этой проблемы состоят в том, чтобы создать цикл, который вызывает метод обработчика, при условии, что значение Socket.AcceptAsync возвращает значение false и разбить цикл (чтобы разрешить отложенную обработку), если значение указывает, что операция выполняется асинхронно (true). Однако это решение также вызывает уязвимость переполнения стека из-за того, что обратный вызов, связанный с SocketAsyncEventArgs, переданный в Socket.AcceptAsync, имеет в конце метода вызов Socket.AcceptAsync, который также имеет цикл для сразу доступных, синхронно принятых соединений ,

Как вы можете видеть, это довольно солидная проблема, и мне еще предстоит найти хорошее решение, которое не связано с System.Threading.ThreadPool и созданием множества других методов и обработки планирования. Насколько я вижу, асинхронная модель сокета, относящаяся к Socket.AcceptAsync, требует больше, чем показано в примерах в MSDN.

У кого-нибудь есть чистое и эффективное решение для обработки сразу ожидающих соединений, которые синхронно принимаются от Socket.AcceptAsync, не вступая в создание отдельных потоков для обработки соединений и без использования рекурсии?

+0

Да, примеры на MSDN не всегда является лучшим (или лучшей практики, по этому вопросу). Вы можете уточнить свой вопрос? Вы ищете способ работы с asyc сокетами, которые не вызовут StackOverflowExceptoin? – Oded

+0

Это правильно; Я ищу нерекурсивный способ обработки сразу ожидающих соединений. –

+0

См. Также http://stackoverflow.com/questions/2002143/asynchronous-sockets-handling-false-socket-acceptasync-values ​​ – Lucero

ответ

7

я бы не использовать AcceptAsync, а BeginAccept/EndAccept и правильно реализовать обычный шаблон асинхронизации, то есть проверять на наличие CompletedSynchronously, чтобы избежать обратных вызовов в потоке обратного вызова при завершенных операциях.

Смотрите также AsyncCallBack CompletedSynchronously


Edit относительно требований использовать AcceptAsync:

MSDN documentation явно говорит о том, что обратный вызов не будет вызываться для операций, выполненных синхронно. Это отличается от обычного асинхронного шаблона, где обратный вызов всегда вызывается.

Возвращает true, если операция ввода-вывода не ожидается. Событие SocketAsyncEventArgs.Completed по параметру e будет поднято после завершения операции . Возвращает false, если операция ввода-вывода завершена синхронно. SocketAsyncEventArgs.Completed событие от параметра е не будет поднято и й объект, переданный в качестве параметра может быть рассмотрен сразу же после того, как вызов метода возвращает для получения результата операции.

В настоящее время я не вижу, как цикл не будет решать проблему переполнения стека. Может быть, вы можете быть более конкретным в коде, который вызывает проблему?


Edit 2: Я имею в виду код, как это (только в отношении AcceptAsync, остальное было просто получить рабочее приложение, чтобы попробовать его с):

static void Main(string[] args) { 
    Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
    listenSocket.Bind(new IPEndPoint(IPAddress.Loopback, 4444)); 
    listenSocket.Listen(100); 
    SocketAsyncEventArgs e = new SocketAsyncEventArgs(); 
    e.Completed += AcceptCallback; 
    if (!listenSocket.AcceptAsync(e)) { 
     AcceptCallback(listenSocket, e); 
    } 
    Console.ReadKey(true); 
} 

private static void AcceptCallback(object sender, SocketAsyncEventArgs e) { 
    Socket listenSocket = (Socket)sender; 
    do { 
     try { 
      Socket newSocket = e.AcceptSocket; 
      Debug.Assert(newSocket != null); 
      // do your magic here with the new socket 
      newSocket.Send(Encoding.ASCII.GetBytes("Hello socket!")); 
      newSocket.Disconnect(false); 
      newSocket.Close(); 
     } catch { 
      // handle any exceptions here; 
     } finally { 
      e.AcceptSocket = null; // to enable reuse 
     } 
    } while (!listenSocket.AcceptAsync(e)); 
} 
+0

К сожалению, BeginAccept/EndAccept не соответствует моим потребностям, это связано с требованием создания экземпляров объектов IAsyncResult для каждой операции. Мой сервер требует большого объема операций, и профилирование показало, что это была стрессовая точка в приложении. И даже тогда предоставленное решение поражает цель синхронного завершения, вызывая обратный вызов в другом потоке или имея какую-то странную систему хранения вызовов до скорости, из отдельного потока. Другой ответ, который кто-то сделал, по существу идентичен вашему. –

+0

В ответ на ваше изменение проблема возникает в случае обращения к сразу доступным SocketAsyncEventArgs, вызывая обработчик AcceptAsync из того же потока, например, вы проверяете, является ли возвращаемое значение ложным, а затем просто вызывают обработчик с аргументом SocketAsyncEventArgs, который использовался в Socket.AcceptAsync.И затем внутри обработчика accept вы используете тот же шаблон в конце, чтобы обеспечить правильную обработку всех соединений (синхронных или асинхронных). –

+0

В ответ на второе редактирование с образцом кода, это именно то, что я имел в виду, когда отправил свой ответ на мой вопрос. Спасибо за публикацию кода, потому что некоторые люди, возможно, не поняли, что я пытаюсь получить через текстовое. Я считаю, что именно ваш ответ вызвал мою мысль о том, как решить проблему в первую очередь; еще раз спасибо. Хотя я должен сказать, что смешивание синхронной и асинхронной работы сокетов является неприятным, хе-хе. Но эй, у меня есть идея. –

1

Я не смотрел внимательно, но он пахнет, как это могло бы быть полезным (смотрите раздел под названием «стек погружение»):

http://blogs.msdn.com/b/mjm/archive/2005/05/04/414793.aspx

+0

Мы не говорим о Windows Communication Foundation. Хотя это аналогичная проблема, это не имеет значения. Похоже, что они просто побеждают цель синхронного выполнения действий, запустив обработчик в отдельном потоке. В свою очередь, наличие в отдельном потоке может привести к переполнению стека в потоке обработчика. –

+0

Чтобы быть ясным, часть, которую я вызвал, касается не только WCF, но и написания любого цикла асинхронизации с шаблоном начала. – Brian

+0

Я перечитал его несколько раз и сделал чек в нем, а затем уточнил мой комментарий. Приношу свои извинения, если он сойдет с ума. Это просто не то, что я ищу, и я ценю ваш вклад. –

3

У меня есть разрешили эту проблему, просто изменив размещение петли. Вместо того, чтобы рекурсивно вызывать обработчик accept изнутри, обертывание кода в цикле do-while с условием «! Socket.AcceptAsync (args)» предотвращает переполнение стека.

Причина этого заключается в том, что вы используете поток обратного вызова для обработки доступных сразу соединений, прежде чем пытаться асинхронно ждать появления других подключений. Он эффективно использует объединенную нить.

Я ценю ответы, но почему-то никто из них не щелкнул со мной и на самом деле не решил проблему. Однако, кажется, что-то в этом побудило мой разум придумать эту идею. Это позволяет избежать ручной работы с классом ThreadPool и не использует рекурсию.

Конечно, если у кого-то есть лучшее решение или даже альтернатива, я был бы рад это услышать.

+0

Звучит очень похоже на то, что я взламывал вместе, когда писал этот комментарий. Вот почему я спросил, как цикл не решит проблему, так как этот обратный вызов не вызывается, когда вы зацикливаете, нет рекурсии ... – Lucero

+0

@ Майкл: Я понимаю, что это почти 4 года, но я сейчас занимаюсь тот же вопрос, и я не мог точно понять, что вы сделали. Вы помните? – Rotem

+0

@ Ротем Да, я! В основном я изначально вызывал «AcceptAsync» из обратного вызова события «Завершено», и в случае, если он завершился синхронно, я просто вызывал один и тот же обратный вызов. Это вызовет переполнение стека, если многие вызовы будут выполняться синхронно, как это часто бывает при частых нагрузках. Вместо того, чтобы делать это таким образом, я просто сделал цикл как: 'while (! Acceptor.AcceptAsync (state)) {/ * do stuff * /}' –

0
newSocket.Send(Encoding.ASCII.GetBytes("Hello socket!")); 
newSocket.Disconnect(false); 
newSocket.Close(); 

Проблема с этим фрагментом выше заключается в том, что это заблокирует вашу следующую операцию приема.

Лучший способ, как это:

while (true) 
{ 
    if (e.SocketError == SocketError.Success) 
    { 
     //ReadEventArg object user token 
     SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop(); 
     Socket socket = ((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket; 

     if (!socket.ReceiveAsync(readEventArgs)) 
     ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessReceiveEx), readEventArgs); . 
    } 
    else 
    { 
     HadleBadAccept(e); 
    } 

    e.AcceptSocket = null; 

    m_maxNumberAcceptedClients.WaitOne(); 
    if (listenSocket.AcceptAsync(e)) 
     break; 
} 
+0

Это комментарий для моего 2-летнего downvote. Я проигнорировал это, потому что это не имеет смысла для контекста вопроса. Я не приводил пример кода, поэтому кажется, что вы отвечаете на ответ на этот вопрос, а не на сам вопрос. Это должно было быть предложение из двух предложений относительно принятого ответа. –

Смежные вопросы