我正在查看使用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中一次执行一次。
答案 0 :(得分:1)
查看代码:
if (!willRaiseEvent)
{
this.ProcessAccept(acceptEventArg);
}
虽然我还没有理解整个机制,但是willRaiseEvent == true会明确地结束递归,所以我猜这种情况会发生,所以它不是无休止的递归。
答案 1 :(得分:1)
如果无法立即满足AsyncAccept(或任何AsyncXXX操作),则它将返回true,表示操作将异步完成。发生这种情况时,callback-event最终会在线程池线程上触发。即使它编组回到UI线程(因为它是在那里启动),它也会通过帖子完成。
AsyncAccept 高度可能会返回true,因为除非有真正挂起的套接字连接(请参阅Listen中的backlog),否则您正在等待客户端连接。
因此,StartAccept()将在不调用ProcessAccept的情况下退出,并且当它(和如果)触发时,ProcessAccept可能会在不同的线程上。
答案 2 :(得分:0)
这一切都取决于willCauseEvent
标志,当设置为true时会中断递归。
如果没有未决连接,该标志可能设置为true。
答案 3 :(得分:0)
很难从上下文中确定,但看起来像第一个只在
时直接调用第二个Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg);
if (!willRaiseEvent)
{
因此我猜大多数时候它会在完成时引发一个事件(来自不同的回调线程)?
(另见
的'堆叠潜水'部分http://blogs.msdn.com/mjm/archive/2005/05/04/414793.aspx
这似乎是一种类似的事情。)
答案 4 :(得分:0)
ProcessAccept()
始终调用StartAccept()
但反之则不然。
在StartAccept()
中,只有ProcessAccept()
设置为true才会调用willRaiseEvent
。那就是你在那里退出无限递归。
如果您怀疑无限递归(在单个递归函数或其中一个乒乓函数中,您如此雄辩地说: - ),您总是会寻找可能的退出点。 -