在读取消息之前,用于TcpClient的缓冲区会被覆盖

时间:2012-02-17 10:23:32

标签: c# tcpclient

要通过TCP连接为我的应用程序发送数据,我使用一段简单的代码:

public void Send(byte[] message)
{
    if (socket != null)
    {
        if (stream != null)
        {
            stream.Write(message, 0, message.Length);
            if (receiveThread == null)
            {
                StartReceiver();
            }
        }
    }
}

套接字是TcpClient类的实例,流是关联的流实例。 StartReceiver()启动一个Thread,正如方法所暗示的那样,它接收发送给应用程序的数据。

要接收数据,我使用:

private void ReceiveLoop()
{
    DataReceivedStruct drs;
    try
    {
        for (; ; )
        {
            if (stream != null)
            {
                drs = new DataReceivedStruct();
                drs.stream = stream;
                drs.waitHandle = are;
                stream.BeginRead(readBuffer, 0, readBuffer.Length, DataReceived, drs);
                Console.WriteLine("Waiting to be allowed to continue");
                are.WaitOne();
                Console.WriteLine("Allowed, continuing loop");
            }
            else
            {
                Thread.Sleep(5);
            }
        }
    }
    catch (SocketException e)
    {
        DispatchRaiseException(e);
    }
    catch (Exception e)
    {
        DispatchRaiseException(e);
    }
}

同样,使用的流是TcpClient类对象的上述流实例。 readBuffer对象是byte[1024]。给BeginRead的回调如下所示:

private void DataReceived(IAsyncResult result)
{
    DataReceivedStruct drs = (DataReceivedStruct)result.AsyncState;
    NetworkStream used = drs.stream;
    AutoResetEvent handle = drs.waitHandle;
    used.EndRead(result);
    DispatchRaiseReceived(readBuffer);
    Console.WriteLine("Signalling allowance of continue for loop");
    handle.Set();
}

它结束对流的读取操作并传递readBuffer中的数据集。

原则上这是有效的。我可以从应用程序发送和接收数据。应用程序的接收端只有一个问题。当消息发送到应用程序时,将调用BeginRead函数,之后回调将触发并以EndRead结束读取操作,并传递数据以进行进一步处理。这适用于一次一条消息。但是在第一条消息触发BeginRead后直接发送另一条消息时,它会变得更有趣。然后会发生的是第一条消息的EndRead尚未发生,因此第一条消息的数据被第二条消息写入,导致数据不正确。

我应该停止使用BeginRead / EndRead并使用阻止Read操作来接收数据吗?或者是否可以使用BeginRead / EndRead锁定流,以便在处理完第一条消息之前不会收到第二条消息?

1 个答案:

答案 0 :(得分:1)

IMO,这里的问题是考虑循环,因此需要一个继续标志。这不会扩展,因为它需要每个连接一个线程。

应该做的是:

  • 获取一些要发送的数据
  • 开始接收异步(我使用ReceiveAsync,但BeginRead也应该工作)
  • 然后退出! (没有循环)

在回调中:

  • 处理片段(或缓冲它)
  • 必要时开始接收
  • 并退出!

如果您乐意替换几个不同的缓冲区(我使用一个小型微池),您可以交换“进程”,以便您可以继续并行处理。但是你绝对需要每次读取不同的缓冲区,以防止覆盖数据。这通常只适用于异常高读取方案。

如果它有帮助,我正在编写一个用于编写简单的TCP客户端/服务器方案的库,而不必担心所有蹩脚的实现细节,我计划一旦稳定发布为开源;具有广泛的对象池/重用,完全异步使用(使用绑定到完成端口的3.5 API)等。