我正在尝试将一些旧的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())
调用创建的StreamPipeReader
的TryRead
实现不是ReadAsync
的同步对应物-在TryRead
返回其获取的数据之前,必须先调用ReadAsync
。在TryRead
上无休止循环将不起作用。GitHub上有一个关于由此引起的混乱的问题。
推荐的解决方案是将代码重写为async
并符合ReadAsync
的条件,因为管道设计为异步使用。
至少获得同步读取的另一个解决方案是使用Pipelines.Sockets.Unficial NuGet包,该包在不需要Stream
s的情况下在Socket
s上实现SocketConnection
s。它的管道恰好具有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()
取消它,否则,下一个呼叫将抛出"阅读已在进行中";。请注意,如果您想最终写入数据,您必须刷新它,并且没有可用的同步刷新,这也是我不建议这样做的另一个原因。