【问题标题】:c# AsyncSockets recursion in when reading from client socketc# AsyncSockets 从客户端套接字读取时递归
【发布时间】:2019-09-10 13:59:11
【问题描述】:

我正在关注有关创建异步套接字服务器 (here) 的 MSDN 教程。我需要服务器能够持续收听来自客户端的消息,在查看了herehere 的答案后,我得到了这样的结果:

        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()

【问题讨论】:

  • 你没有做递归。您在返回之前使用 AsyncCallback 注册一个新事件。堆栈大小不会增长。
  • @jdweng 好的,那么这些解决方案很有意义。谢谢!

标签: c# sockets asynchronous


【解决方案1】:

我的问题是,由于递归调用 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 设备(尤其是网络适配器)发送数据的速度。接收操作通常只需要另一个或两个回调就可以赶上可用数据。

底线是,只要您按照上面所做的那样实现代码,在您完全处理完当前完成的操作之前不会启动新的接收操作,您应该没有什么可担心的关于。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多