用于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
锁定流,以便在处理第一条消息之前不会接收到第二条消息?
IMO,这里的问题是从循环的角度思考,因此需要一个continue标志。这是不可伸缩的,因为它需要每个连接一个线程。
您应该做的是:
- 获取一些要发送的数据
- 开始接收异步(我使用ReceiveAsync,但BeginRead也应该工作)
- 然后退出!(无环路)
在回调中:
- 处理片段(或缓冲它)
- 必要时开始接收
- 然后退出
如果你愿意替换几个不同的缓冲区(我使用的是一个小的微池),你可以交换"进程",这样你就可以在处理的同时继续读取。但是,每次读取绝对需要不同的缓冲区,以防止数据过度写入。这通常只在异常高读取情况下才有必要。
如果有帮助的话,我正在开发一个库,用于编写简单的TCP客户端/服务器场景,而不必担心所有粗糙的实现细节,我计划在稳定后将其作为开源发布;具有广泛的对象池化/重用、完全异步使用(使用绑定到完成端口的3.5 API)等特点。