gpt4 book ai didi

c# - 超时后阻止来自 C# NetworkStream 的传入数据

转载 作者:行者123 更新时间:2023-12-02 02:39:37 25 4
gpt4 key购买 nike

我有一个奇怪的问题涉及 C# .Net Core 2.2.101 中的 NetworkStreams

我的设置如下:

  • 我有一个仪表的列表
  • 每个仪表都有一个寄存器列表(一个寄存器保存一个值,例如:电压或电流)
  • 仪表通过 RS485 连接到 GSM 调制解调器(与问题无关)
  • 我向调制解调器发送命令以读取特定仪表的特定寄存器

对于每个仪表的每个寄存器,我使用 stream.Write(bytesToSend, 0, bytesToSend.Length); 发送一个命令,要求仪表将保存在特定位置的数据发送给我在仪表中注册。发送后,我立即使用 stream.Read(buffer, 0, buffer.Length); 读取响应。我还设置了一个 5 秒的读取超时,如果在超时之前没有收到响应,它将阻塞并等待 5 秒,然后再继续下一个寄存器。

问题:

发生的事情是,当我向仪表询问数据时,有时会花费太长时间并且会达到超时,之后我会继续向下一个寄存器询问数据,但有时第一个寄存器会在我转到下一个寄存器后回复数据(这意味着 NetworkStream 现在有来自前一个寄存器的数据)。由于我已经在我的 for 循环中继续前进,我的程序认为我从流中读取的数据是针对当前寄存器的,而实际上它是针对上一次迭代的前一个寄存器.这弄乱了进入数据库的数据,因为我为错误的寄存器保存了错误的值。

我的问题是:在我的 for 循环中是否有一种聪明的方法来忽略来自先前迭代的任何传入数据?不幸的是,接收到的数据中没有任何信息这可用于识别数据用于哪个寄存器。

这是我的写入和读取请求的片段:

stream.ReadTimeout = 5000;
stream.WriteTimeout = 2000;

foreach (Meter meter in metersToRead)
{
foreach (Register register in meter.Registers)
{
// Write the request to the meter
stream.Write(bytesToSend, 0, bytesToSend.Length);

// Read response from meter
requestedReadingDataCount = stream.Read(buffer, 0, buffer.Length);

// Extract the value from the response buffer and save the value to the database
...
}
}

我想尝试克隆流并使用克隆流进行有关当前寄存器迭代的通信,这样当我关闭克隆流并移至下一个寄存器后收到响应时,响应将失败因为流已经关闭。 但是,我不确定您是否可以克隆 C# NetworkStream?有人知道吗?

我最后的办法是在我读取每个寄存器的数据后调用数据库,以检查我收到的数据对于该寄存器是否合理,但我担心这可能会减慢程序的速度数据库调用,我将不得不建立一些规则来确定一个值对于当前寄存器是否合理。

任何想法将不胜感激。如果您有任何问题,请告诉我,我会尽力进一步解释。

编辑

这是一个更新的代码片段,以及一张可以更好地解释我遇到的问题的图片。

private async Task ReadMeterRegisters(List<MeterWithRegisters> metersWithRegisters, NetworkStream stream)
{
stream.ReadTimeout = 5000; /* Read timeout set to 5 seconds */
stream.WriteTimeout = 2000; /* Write timeout set to 2 seconds */

foreach (Meter meter in metersToRead)
{
foreach (Register register in meter.Registers)
{
// Instantiate a new buffer to hold the response
byte[] readingResponseDataBuffer = new byte[32];

// Variable to hold number of bytes received
int numBytesReceived = 0;

try
{
// Write the request to the meter
stream.Write(bytesToSend, 0, bytesToSend.Length);

// Read response from meter
numBytesReceived = stream.Read(buffer, 0, buffer.Length);
}
catch (IOException) /* catch read/write timeouts */
{
// No response from meter, move on to next register of current meter
continue;
}

// Extract the value from the response buffer and save the value to the database
...
}
}
}

Description of code snippet

最佳答案

听起来这里的问题是在超时情况下,读取操作在未来的某个时刻仍在完成,但写入旧缓冲区。如果是这种情况,也许最简单的选择是在超时的情况下不重用读取缓冲区(意思是:将新的 byte[] 分配给 buffer),并考虑网络流被烧毁(因为你现在不知道内部状态是什么)。

另一种方法是在知道有数据之前不读取;您不能从 NetworkStream 执行此操作,但在 Socket 上,您可以检查 .Available 以查看是否有要读取的数据;这样,您就不会执行模棱两可的读取。您还可以在套接字上执行零长度读取(至少在大多数操作系统上);如果你传递一个零长度缓冲区,它将阻塞直到超时或直到数据可用,但不会消耗任何数据(这个想法是如果你发现你跟随一个零长度读取和一个非零长度读取数据已变得可用)。

在更一般的情况下:如果切换到异步 IO 而不是同步 IO,您可能会发现在这里可以获得更好的吞吐量;您甚至可以使用数组池作为缓冲区。对于处理大量连接,异步 IO 几乎总是可行的方法。

关于c# - 超时后阻止来自 C# NetworkStream 的传入数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60655867/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com