2009-02-22 159 views
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? 
     } 

看看最后一个线。它再次调用顶层函数。这是如何通过在这两个函数之间来回跳动来溢出堆栈的?这似乎是尾端递归,但这不是Haskell,所以我不明白这是如何工作的。

这是我的理解,这些不是在线程中被解雇,而是由cpu一次执行一个。

回答

1

如果无法立即满足AsyncAccept(或任何AsyncXXX操作),则它将返回true,表示操作将异步完成。发生这种情况时,回调事件最终会在线程池线程上触发。即使它编组回到UI线程(因为它是在那里启动的),它也会通过一个帖子来完成。

AsyncAccept是高度有可能返回true,因为除非有真正的挂起(见Listen积压),你正在等待一个客户端连接套接字连接。

因此,StartAccept()只会在不调用ProcessAccept的情况下退出,并且ProcessAccept在(和if)触发时会简单地退出,可能会在不同的线程中。

1

看代码:

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

虽然我还不了解整个机制,willRaiseEvent == true,将清楚地结束递归,所以我想这种情况发生,所以它不是无限递归。

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时才被调用。那就是你从那里的无限递归中退出。

如果您怀疑无限递归(无论是单递归函数还是其中一个乒乓函数),您都会查找可能的退出点,正如您雄辩地说的那样:-)。