c# - NetworkStream 异步读取 -> 取消
问题描述
目前我尝试从网络流中读取和写入异步。我的软件是客户端部分,服务器可以自己发送信息或响应我发送给他的命令。
所以我需要一个插座
- 一直读取(以防服务器发送状态信息)
- 当我想发送命令时停止读取(命令可以是具有多个写入和读取操作的数据序列)
所以我认为创建一个信号量和一个后台任务来处理服务器发送的消息是一个很好的方法,如果我想发送一个命令,我会阻止信号量并完全访问对套接字的读/写操作。
这是我目前所做的。
private TcpClient _tcpClient = new TcpClient();
protected SemaphoreSlim ClientSemaphore { get; } = new SemaphoreSlim(1, 1);
public async Task ConnectAsync()
{
if (_tcpClient.Connected)
{
await DisconnectAsync();
}
await _tcpClient.ConnectAsync(Hostname, RemotePort);
//here the background Task is started
_ = AutoReceiveMessages();
}
private async Task AutoReceiveMessages()
{
while (_tcpClient.Connected)
{
//enter and lock semaphore
await ClientSemaphore.WaitAsync();
try
{
//read from socket until timeout (ms)
var msg = await ReadFromSocket(2000);
foreach (var cmd in SplitMessageInTelegrams(msg))
{
Console.WriteLine("MESSAGE --> " + cmd);
}
}
catch (Exception ex)
{
}
finally
{
//release semaphore
ClientSemaphore.Release();
}
}
}
private async Task<string> ReadFromSocket(double timeout = 0)
{
var buf = new byte[4096];
var stream = _tcpClient.GetStream();
//read from stream or timeout
var amountReadTask = stream.ReadAsync(buf, 0, buf.Length);
var timeoutTask = Task.Delay(TimeSpan.FromMilliseconds(timeout));
await Task.WhenAny(timeoutTask, amountReadTask)
.ConfigureAwait(false);
//timeout
if (!amountReadTask.IsCompleted)
{
throw new TimeoutException("Timeout");
}
//no timeout
return Encoding.ASCII.GetString(buf, 0, amountReadTask.Result);
}
但这并不像我预期的那样工作......我使用这种方法向服务器发送消息,在 WireShark 中我看到服务器响应相同的消息
protected async Task SendTelegramAsync(ITelegram telegram)
{
await ClientSemaphore.WaitAsync();
try
{
_ = telegram ?? throw new ArgumentException($"{nameof(telegram)}");
if (!_tcpClient.Connected) throw new InvalidOperationException("Socket not connected!");
var buf = new byte[4096];
var stream = _tcpClient.GetStream();
var msg = Encoding.ASCII.GetBytes("\x02" + telegram.GetCommandMessage() + "\x03");
Console.WriteLine("WRITE --> " + msg);
await stream.WriteAsync(msg, 0, msg.Length);
//comment AutoReceiveMessage and remove comment from this
//and I get responses from the server
//var test = await ReadFromSocket(2000);
}
finally
{
ClientSemaphore.Release();
}
}
我知道在这种情况下我不需要信号量,但后来我想创建序列,因此一个命令由多个写入和读取组成,并且只要执行该命令,我就不想使用 AutoReceiveMessages 方法。
现在的问题是
- 如果我这样使用它,我永远不会得到响应,即使wireshark告诉我服务器已经响应,ReadFromSocket 方法总是会超时
- 但是,如果我禁用 AutoReceiveMessages(只需注释 _ = AutoReceiveMessages())并直接在 SendTelegramAsync() 中使用 ReadFromSocket,一切都会按预期工作,这样会更好。
所以我认为问题与后台任务和 ReadAsync 有关,但我无法弄清楚......
解决方案
知道了!
stream.DataAvailable 是你的朋友(或我的朋友 :))。
如果我在 ReadAsync 之前检查 DataIsAvailable 那么我就没有问题了。
if (_tcpClient.GetStream().DataAvailable)
var msg = await ReadFromSocket(DEFAULT_TIMEOUT);
推荐阅读
- gitlab - Gitlab CI lint:作业配置应该是一个包含字符串和字符串数组的数组
- swift - 如何使用 UIViewControllerRepresentable 在 SwiftUI 中呈现 UICollectionView
- postgresql - PotgresSQL INSERT INTO SELECT CASE 问题
- angular - 如何在 HTML 中获取按钮切换状态
- xml - 模块化 optaplanner 基准配置
- reactjs - Typescript 编译器无法检查 react-redux 连接 HOC
- ios - 当设备在 IOS 中离线后在线时,没有收到任何 Firebase 云消息通知
- python - 查找最大宽度“斜坡”(leetcode)
- c - cs50 pset1 credit 为什么要使用模数?
- java - 致命异常:com.google.maps.api.android.lib6.gmm6.vector.ct 处的 java.lang.ArrayIndexOutOfBoundsException