C#异步套接字在从客户端套接字读取时的递归问题

3
我正在按照MSDN教程创建异步Socket服务器(这里)。我需要服务器能够持续监听来自客户端的消息,经过查看这里这里的答案,我最终得到了以下代码:
        public static void ReadCallback(IAsyncResult ar)
        {
            String content = String.Empty;

            StateObject state = (StateObject)ar.AsyncState;
            Socket handler = state.workSocket;

            int bytesRead = handler.EndReceive(ar);

            if (bytesRead > 0)
            { 
                state.sb.Append(Encoding.ASCII.GetString(
                    state.buffer, 0, bytesRead));

                content = state.sb.ToString();
                if (content.IndexOf("<EOF>") > -1)
                {
                    // Message received, do something 
                    // ...
                }
                else
                {
                    // Not all data received. Get more
                    // ...
                }
            }

            // Continue waiting
            handler.BeginReceive(state.buffer, 0, SocketState.BufferSize, 0,
            new AsyncCallback(ReadCallback), state);            
        }


我的问题是,由于对ReadCallback()进行了递归调用,这种方法会无限增加堆栈吗?

3
你没有在进行递归,而是在返回之前使用AsyncCallback注册一个新事件。堆栈大小不会增加。 - undefined
@jdweng 好的,那些解决方案确实有道理。谢谢! - undefined
1个回答

3
我的问题是,由于ReadCallback()方法中存在递归调用,这种做法会使堆栈无限增长吗?
不会。按照定义,递归调用涉及调用另一个方法,该方法直接或间接地调用当前方法而该调用仍在进行中
使用任何可用于套接字I/O的许多异步技术之一时(例如通过调用Socket.BeginReceive()方法启动新的接收操作),您所调用的方法通常不会在呼叫正在进行中时导致当前方法再次被调用。当有更多数据可以接收时,将稍后调用当前方法。
但是,有一个重要的警告:
  • 即使调用不是递归的,也不能保证不会调用可重入回调函数。也就是说,如果数据足够快地可用,不同的线程可能会在当前回调完成之前调用您的回调以处理接收到的新数据缓冲区。
通常,这是通过在所有数据处理完成后(就像您发布的代码示例一样)等到回调结束后再次调用BeginReceive()来解决的。这确保即使方法被重新输入调用,当前调用唯一剩下的事情是返回给调用者,因此它不会与随后的调用发生冲突。
还有另一个理论上的“陷阱”,但我相当确定它在这里不适用。 .NET使用IOCP实现套接字API的异步I/O。默认情况下,IOCP将始终为完成分配新线程,从而防止递归。但是,如果IOCP客户端使用了FILE_SKIP_COMPLETION_PORT_ON_SUCCESS选项,则IOCP可能会在没有将完成排队到完成端口的情况下递归调用完成回调,因为数据在启动I/O操作时已经可用并等待读取。
我认为.NET不使用此选项,因此您不会遇到这种情况。但请注意,即使您确实这样做了,递归也不太可能变得非常深,因为CPU通常可以比任何I/O设备(特别是网络适配器)发送数据更快地处理数据。只需要再次回调一两次即可使接收操作赶上可用数据。
底线是,只要像上面一样实现代码,在完全处理当前完成的操作之前不会启动新的接收操作,就不必担心。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接