Как это не вызывает переполнение стека?


4

Я смотрю на исходный код для сервера с помощью SocketAsyncEventArgs, и я пытаюсь выяснить, как это не будет вызывать переполнение стека:

Так что этот код называется, чтобы сокет, чтобы принять входящее соединение (прокрутите вниз до нижней части, чтобы увидеть, что я имею в виду):

/// <summary> 
/// Begins an operation to accept a connection request from the client. 
/// </summary> 
/// <param name="acceptEventArg">The context object to use when issuing 
/// the accept operation on the server's listening socket.</param> 
private void StartAccept(SocketAsyncEventArgs acceptEventArg) 
{ 
    if (acceptEventArg == null) 
    { 
     acceptEventArg = new SocketAsyncEventArgs(); 
     acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(OnAcceptCompleted); 
    } 
    else 
    { 
     // Socket must be cleared since the context object is being reused. 
     acceptEventArg.AcceptSocket = null; 
    } 

    this.semaphoreAcceptedClients.WaitOne(); 
    Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg); 
    if (!willRaiseEvent) 
    { 
     this.ProcessAccept(acceptEventArg); 
    } 
} 

Затем этот код вызывается когда соединение фактически принято (см, последнюю строку):

/// <summary> 
     /// Process the accept for the socket listener. 
     /// </summary> 
     /// <param name="e">SocketAsyncEventArg associated with the completed accept operation.</param> 
     private void ProcessAccept(SocketAsyncEventArgs e) 
     { 
      if (e.BytesTransferred > 0) 
      { 
       Interlocked.Increment(ref this.numConnectedSockets); 
       Console.WriteLine("Client connection accepted. There are {0} clients connected to the server", 
        this.numConnectedSockets); 
      } 

      // Get the socket for the accepted client connection and put it into the 
      // ReadEventArg object user token. 
      SocketAsyncEventArgs readEventArgs = this.readWritePool.Pop(); 
      readEventArgs.UserToken = e.AcceptSocket; 

      // As soon as the client is connected, post a receive to the connection. 
      Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs); 
      if (!willRaiseEvent) 
      { 
       this.ProcessReceive(readEventArgs); 
      } 

      // Accept the next connection request. 
      this.StartAccept(e); // <==== tail end recursive? 
     } 

Посмотрите на последний линия. Он снова вызывает верхнюю функцию. Как это не переполняет стек путем ping-ponging назад и вперед между этими двумя функциями? Кажется, это рекурсия конца хвоста, но это не Haskell, поэтому я не вижу, как это будет работать.

Это было мое понимание, что они не были выпущены в потоки, но только что выполнялись по одному за процессор.

1

Если AsyncAccept (или любая операция AsyncXXX, если на то пошло) не может быть удовлетворен немедленно, то он вернет true, указывая на то, что операция завершится асинхронно. Когда это произойдет, событие обратного вызова, в конечном счете, будет запущено в потоке потока. Даже если он возвращает маршала к потоку пользовательского интерфейса (потому что он был инициирован там), он сделает это через сообщение.

AsyncAccept является весьма скорее всего, вернется так, потому что, если нет гнезда подключения действительно ожидающие (см накопившихся в Listen), вы в ожидании клиента для подключения.

Следовательно, StartAccept() просто выйдет без вызова ProcessAccept, а ProcessAccept, когда (и если) он срабатывает, вероятно, будет в другом потоке.


1

Посмотрите на код:

if (!willRaiseEvent) 
{ 
    this.ProcessAccept(acceptEventArg); 
} 

Хотя я до сих пор не понимаю, весь механизм, willRaiseEvent == верно будет ясно закончить рекурсию, так что я предполагаю, что это происходит, так что это не бесконечная рекурсия.


0

Все зависит от флага willCauseEvent, который, когда установлен в true, прерывает рекурсию. Этот флаг, вероятно, установлен в true, если нет ожидающего подключения.


0

Трудно определить из контекста, но выглядит как первый только называет второй непосредственно при

Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg);  
if (!willRaiseEvent)  
{ 

и, таким образом, я предполагаю, что большую часть времени она вызывает событие на завершение (от другого обратного вызова нить)?

(Смотрите также «стек погружения» часть

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

, который, кажется, подобного рода вещи.)


0

ProcessAccept()всегда звонки StartAccept(), но обратное не верно ,

В StartAccept(), ProcessAccept() вызывается только в том случае, если для параметра willRaiseEvent установлено значение true. Это ваш выход из бесконечной рекурсии прямо там.

Вы всегда ищете возможные точки выхода, если вы подозреваете бесконечную рекурсию (либо в одной рекурсивной функции, либо в одном из ваших пинг-понга, как вы так красноречиво выразились :-).