无法让 PipeReader.TryRead 在从套接字读取时工作



我正在尝试将一些旧的C#套接字代码转换为使用新的PipeReader库。

我有一个线程,它通过套接字连接到远程m/c,并读取(永远(和处理远程端点发送的任何数据。我想看看将它转换为PipeReader和堆叠缓冲区会在多大程度上提高内存/性能。我有这样的东西:

// setup the connection to the remote endpoint
// and start the data flow
using var connection = new TcpClient(_connectionInfo.Name, _connectionInfo.Port) {
ReceiveTimeout = TimeSpan.FromMilliseconds(500).Milliseconds,
SendTimeout = TimeSpan.FromMilliseconds(500).Milliseconds,
};
...
// now attach a pipereader to the end of the socket to suck the data out
var reader = PipeReader.Create(connection.GetStream(), new StreamPipeReaderOptions(bufferSize: BufferSize));
while (!_stopToken.IsCancellationRequested) {
if (reader.TryRead(out ReadResult result)) {
...
...
reader.AdvanceTo(result.Buffer.End);
}else {
_stopToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(5));
}
}

TryRead从不成功-它总是返回false。如果我查看连接到日志的远程组件,那么我可以看到";选择超时";根据我的IP地址记录错误,我想这表明我的端没有任何侦听内容?

但奇怪的是,如果我使用ReadAsync,那么一切都可以!

我使用异步方式做事没有问题——我已经在一个线程中了,所以不需要它——但我很想知道我缺少了什么。

显然,由PipeReader.Create(connection.GetStream())调用创建的StreamPipeReaderTryRead实现不是ReadAsync的同步对应物-在TryRead返回其获取的数据之前,必须先调用ReadAsync。在TryRead上无休止循环将不起作用。GitHub上有一个关于由此引起的混乱的问题。

推荐的解决方案是将代码重写为async并符合ReadAsync的条件,因为管道设计为异步使用。


至少获得同步读取的另一个解决方案是使用Pipelines.Sockets.Unficial NuGet包,该包在不需要Streams的情况下在Sockets上实现SocketConnections。它的管道恰好具有TryRead的实现,该实现按预期工作。你的代码可能是这样的:

// setup the connection to the remote endpoint
// and start the data flow
using var connection = new TcpClient(_connectionInfo.Name, _connectionInfo.Port) {
ReceiveTimeout = TimeSpan.FromMilliseconds(500).Milliseconds,
SendTimeout = TimeSpan.FromMilliseconds(500).Milliseconds,
};
...
// now use a SocketConnection to suck the data out
var reader = SocketConnection.Create(connection.Client/*aka Socket*/).Input;
while (!_stopToken.IsCancellationRequested) {
if (reader.TryRead(out ReadResult result)) {
...
...
reader.AdvanceTo(result.Buffer.End);
}else {
_stopToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(5));
}
}

不要忘记提前您的阅读器或用CancelPendingRead()取消它,否则,下一个呼叫将抛出"阅读已在进行中";。请注意,如果您想最终写入数据,您必须刷新它,并且没有可用的同步刷新,这也是我不建议这样做的另一个原因。

最新更新