试图使代码异步,但不阻塞我的程序跑了

本文关键字:我的 程序 代码 异步 | 更新日期: 2023-09-27 18:09:06

编写一个客户端/服务器程序,以前使用这样的调用:

TcpListener.AcceptTcpClient() NetworkStream.Read() NetworkStream.Write()

TcpListener.AcceptTcpClient()工作良好,因为它的回调函数只在连接建立后调用。

然而,BeginRead()不能正常工作。由于某种原因,即使没有发送数据,它也会执行并继续执行。不知道发生了什么。NetworkStream.Read()阻塞正常,但这不是…

下面是在建立连接时创建的非工作线程:

private void HandleClientCommunication(object client)
{
    TcpClient tcpClient = (TcpClient)client;
    NetworkStream clientStream = tcpClient.GetStream();
    try
    {
        byte[] header = new byte[4];
        clientStream.BeginRead(header, 0, header.Length, Read, new StateData(clientStream, header)); // Runs even if no data is sent
        int dataLength = BitConverter.ToInt32(header, 0); // Continues to this, even if no packets are sent..
        byte[] data = new byte[dataLength]; // dataLength is 0 still of course because none of the code is blocking
        clientStream.BeginRead(data, 0, dataLength, Read, new StateData(clientStream, data));
    }
    catch (IOException e)
    {
        Console.WriteLine(e.InnerException.Message);
    }
    // not relevant past here
}

这里也是Read方法。不确定是否需要找出问题,但我将包括它,无论如何:

private void Read(IAsyncResult async)
{
    StateData stateData = (StateData)async.AsyncState;
    stateData.BytesRead += stateData.Stream.EndRead(async);
    if (stateData.BytesRead < stateData.Bytes.Length)
    {
        stateData.Stream.BeginRead(stateData.Bytes, 0, stateData.Bytes.Length, Read, stateData);
    }
    else
    {
        string message = Encoding.ASCII.GetString(stateData.Bytes);
        Console.WriteLine(message);
    }
}

所以是的,我只是试图建立一个客户端服务器与异步调用交互,并有每个"包"包括一个头与数据的长度作为一个int,然后在那之后的数据本身。不幸的是,事情似乎不工作,我不知道是什么问题。

似乎我必须包括一些手动形式的阻塞,但这不违背了使用异步调用的目的吗?为什么不回到常规的同步阻塞调用,比如Read()AcceptTcpClient()呢?

我想我没有完全理解这里的事情

试图使代码异步,但不阻塞我的程序跑了

然而,BeginRead()不能很好地工作。由于某种原因,即使没有发送数据,它也会执行并继续执行。不知道发生了什么。NetworkStream.Read()阻塞正常,但这不是…

我想你误解了BeginRead的意思。并不意味着阻塞——它发出异步读取,在读取完成时执行给定的回调。意味着立即返回——所以你的头数据为空也就不足为奇了。

基本上,任何需要数据的工作都应该在回调中-而不是在调用BeginRead的同一个方法中。你的第一个回调应该转换头数据,然后BeginRead再次用不同的回调来读取消息数据本身…尽管您应该还应该记住,任何Read/BeginRead调用都可能无法读取您请求的所有数据。您试图在回调中考虑到这一点,但您还没有完全做到-因为您总是试图从开始填充缓冲区 -这将覆盖任何现有数据。如果你知道你期望总的数据量,并且你的缓冲区有那么长,你应该从开始读入你已经读了多少字节的缓冲区

请注意,c# 5将使这一切变得简单得多——但如果你等不及,你可能会发现回到多线程(每个客户端一个)上阻塞调用更容易。