【问题标题】:Asynchronous TcpClient not reading all the data in time异步 TcpClient 没有及时读取所有数据
【发布时间】:2013-04-04 12:50:26
【问题描述】:

如果来自服务器的数据不多,或者如果有很多数据并且我取消注释 Thread.Sleep(500) 行,则下面的代码有效。然而,我不想使用Thread.Sleep 行,但如果我删除它,程序将在从服务器读取所有数据之前完成。问题似乎在于EndRead() 在读取所有数据之前不会阻塞,这是可以理解的,应该通过对ReceiveCallback 的递归调用来修复,但事实并非如此。

我有一个在 Linux 上使用 C/C++ 编写的工作客户端。我正在尝试使其与 c# 和 .Net 一起使用。我在 Linux 上使用 MonoDevelop 和 .Net 4.0。感谢您就如何解决此问题提供任何帮助。

cli.send("command");
// Thread.Sleep(500); 
cli.receive();
Console.WriteLine("response: {0}", cli.Response);
    private Static ManualResetEvent recvEvt = new ManualResetEvent(false);

    public static void receive() {
        StateObject state = new StateObject();
        try {
            state.stream = client.GetStream();
            state.stream.BeginRead(state.buffer, 0, state.bufferSize, new AsyncCallback(ReceiveCallback), state);
            recvEvt.WaitOne();
            recvEvt.Reset();
        }
        catch (ArgumentNullException ne) { errorMessage = ne.ToString(); }
        catch (ArgumentException ae) { errorMessage = ae.ToString(); }
        catch (ObjectDisposedException oe) { errorMessage = oe.ToString(); }
        catch (InvalidOperationException ie) { errorMessage = ie.ToString(); }
        catch (SocketException se) { errorMessage = se.ToString(); }
    }


   private static void ReceiveCallback(IAsyncResult ar) {
        StateObject state = (StateObject)ar.AsyncState;
        try {
            int bytesRead = state.stream.EndRead(ar);
            if (bytesRead > 0 ) {
                state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
                response = state.sb.ToString();
                if (state.stream.DataAvailable) {
                    state.stream.BeginRead(state.buffer, 0, state.bufferSize, new AsyncCallback(ReceiveCallback), state);
                    recvEvt.WaitOne();
                    recvEvt.Reset();
                }else
                    recvEvt.Set();
            }else
                recvEvt.Set();
        }
        catch (ArgumentNullException ne) { errorMessage = ne.ToString(); }
        catch (ArgumentException ae) { errorMessage = ae.ToString(); }
        catch (ObjectDisposedException oe) { errorMessage = oe.ToString(); }
        catch (InvalidOperationException ie) { errorMessage = ie.ToString(); }
        catch (SocketException se) { errorMessage = se.ToString(); }
    }

【问题讨论】:

    标签: c# .net asynchronous tcpclient beginread


    【解决方案1】:

    问题是应用程序无法确定是否有更多数据通过网络传入。它只知道它是否收到了任何数据。 DataAvailable 将告诉您的是是否已收到可以从网络流中读取的数据。远端尚未发送给您的数据和已发送但尚未通过网络发送给您的数据对于您的应用程序来说基本上是未知的。

    您必须再次拨打BeginRead,直到您或远端挂断。稍微简化的ReceiveCallback 看起来像这样:

    private static void ReceiveCallback(IAsyncResult ar) {
        StateObject state = (StateObject)ar.AsyncState;
        int bytesRead = state.stream.EndRead(ar);
        if (bytesRead > 0 ) {
            state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
            state.stream.BeginRead(state.buffer, 0, state.bufferSize, new AsyncCallback(ReceiveCallback), state);
        } else {
            response = state.sb.ToString();
        }
    }
    

    一旦接收到数据或连接终止,将调用回调。一旦连接关闭,响应就完成了。

    【讨论】:

    • 谢谢。我已经试过了,它挂了。上面的代码是我发现的唯一一个有效的代码,但需要Thread.Sleep 来处理大数据。我在这方面花了一些时间,通读 MSDN 文档并尝试不同的组合,但似乎无济于事。
    • 如果它挂起,那么听起来远程端仍然打开连接但没有发送更多数据。要么让远端关闭连接,要么实现接收者可以使用的协议,以确定是否已收到完整的消息。
    • 谢谢。那就修好了。我无法修改服务器,但我意识到它在每个命令后都会发送一个特定的提示。我用它来表示输入的结束。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-19
    • 1970-01-01
    • 1970-01-01
    • 2018-07-20
    • 2021-09-14
    • 2018-05-05
    相关资源
    最近更新 更多