试图使代码异步,但没有阻止我的程序正在逃跑?

时间:2011-09-21 06:09:42

标签: c# asynchronous

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

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

我认为我不完全理解这里的事情。

1 个答案:

答案 0 :(得分:3)

  

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

我认为你误解了BeginRead的意图。阻止它不是意味着 - 它发出异步读取,在读取完成时执行给定的回调。 意味着立即返回 - 所以难怪你的标题数据是空的。

基本上,任何需要数据的工作都应该在回调中 - 而不是在调用BeginRead的方法中。您的第一个回调应该转换标头数据,然后使用不同的回调再次BeginRead来读取消息数据本身...尽管您要记住任何Read / BeginRead来电可能无法读取您要求的所有数据。你在回调中尝试来解释这个问题,但你并不在那里 - 因为你总是试图从一开始就填充缓冲区 - 这将是覆盖任何现有数据。如果您知道整体上有多少数据并且缓冲区很长,那么您应该从开始读取缓冲区已经读取了多少字节。

请注意,C#5将使所有这一切变得更加简单 - 但是如果你不能等到那时,你可能会发现更容易回到多个线程上的阻塞调用(每个客户端一个)。