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

本文关键字:覆盖 消息 读取 TcpClient 缓冲区 用于 | 更新日期: 2023-09-27 18:24:53

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

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

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

为了接收数据,我使用:

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锁定流,以便在处理第一条消息之前不会接收到第二条消息?

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

IMO,这里的问题是从循环的角度思考,因此需要一个continue标志。这是不可伸缩的,因为它需要每个连接一个线程。

应该做的是:

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

在回调中:

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

如果你愿意替换几个不同的缓冲区(我使用的是一个小的微池),你可以交换"进程",这样你就可以在处理的同时继续读取。但是,每次读取绝对需要不同的缓冲区,以防止数据过度写入。这通常只在异常高读取情况下才有必要。

如果有帮助的话,我正在开发一个库,用于编写简单的TCP客户端/服务器场景,而不必担心所有粗糙的实现细节,我计划在稳定后将其作为开源发布;具有广泛的对象池化/重用、完全异步使用(使用绑定到完成端口的3.5 API)等特点。