从 .NET 中的 NetworkStream 读取的正确方法是什么 [英] What is the correct way to read from NetworkStream in .NET

查看:28
本文介绍了从 .NET 中的 NetworkStream 读取的正确方法是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在为此苦苦挣扎,但找不到我的代码无法从我也编写的 TCP 服务器正确读取的原因.我正在使用 TcpClient 类及其 GetStream() 方法,但有些东西没有按预期工作.操作无限期阻塞(最后一次读取操作没有按预期超时),或者数据被裁剪(出于某种原因,读取操作返回 0 并退出循环,也许服务器响应不够快).这是实现此功能的三种尝试:

I've been struggling with this and can't find a reason why my code is failing to properly read from a TCP server I've also written. I'm using the TcpClient class and its GetStream() method but something is not working as expected. Either the operation blocks indefinitely (the last read operation doesn't timeout as expected), or the data is cropped (for some reason a Read operation returns 0 and exits the loop, perhaps the server is not responding fast enough). These are three attempts at implementing this function:

// this will break from the loop without getting the entire 4804 bytes from the server 
string SendCmd(string cmd, string ip, int port)
{
    var client = new TcpClient(ip, port);
    var data = Encoding.GetEncoding(1252).GetBytes(cmd);
    var stm = client.GetStream();
    stm.Write(data, 0, data.Length);
    byte[] resp = new byte[2048];
    var memStream = new MemoryStream();
    int bytes = stm.Read(resp, 0, resp.Length);
    while (bytes > 0)
    {
        memStream.Write(resp, 0, bytes);
        bytes = 0;
        if (stm.DataAvailable)
            bytes = stm.Read(resp, 0, resp.Length);
    }
    return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}

// this will block forever. It reads everything but freezes when data is exhausted
string SendCmd(string cmd, string ip, int port)
{
    var client = new TcpClient(ip, port);
    var data = Encoding.GetEncoding(1252).GetBytes(cmd);
    var stm = client.GetStream();
    stm.Write(data, 0, data.Length);
    byte[] resp = new byte[2048];
    var memStream = new MemoryStream();
    int bytes = stm.Read(resp, 0, resp.Length);
    while (bytes > 0)
    {
        memStream.Write(resp, 0, bytes);
        bytes = stm.Read(resp, 0, resp.Length);
    }
    return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}

// inserting a sleep inside the loop will make everything work perfectly
string SendCmd(string cmd, string ip, int port)
{
    var client = new TcpClient(ip, port);
    var data = Encoding.GetEncoding(1252).GetBytes(cmd);
    var stm = client.GetStream();
    stm.Write(data, 0, data.Length);
    byte[] resp = new byte[2048];
    var memStream = new MemoryStream();
    int bytes = stm.Read(resp, 0, resp.Length);
    while (bytes > 0)
    {
        memStream.Write(resp, 0, bytes);
        Thread.Sleep(20);
        bytes = 0;
        if (stm.DataAvailable)
            bytes = stm.Read(resp, 0, resp.Length);
    }
    return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}

最后一个有效",但考虑到套接字已经支持读取超时,将硬编码睡眠放在循环中确实看起来很丑!我需要在 NetworkStreamTcpClient 上设置一些属性吗?问题是否出在服务器上?服务器不会关闭连接,这取决于客户端.上面的也是运行在UI线程上下文(测试程序)里面的,可能跟那个有关吧...

The last one "works", but it certainly looks ugly to put a hard-coded sleep inside the loop considering that sockets already support read timeouts! Do I need to setup some property(ies) on the TcpClient of the NetworkStream? Does the problem resides in the server? The server don't close the connections, it is up to the client to do so. The above is also running inside the UI thread context (test program), maybe it has something to do with that...

有人知道如何正确使用 NetworkStream.Read 来读取数据,直到没有更多数据可用吗?我想我想要的是类似于旧的 Win32 winsock 超时属性... ReadTimeout 等.它尝试读取直到达到超时,然后返回 0...但它有时似乎在数据应该可用时返回 0(或在途中......如果可用,Read 可以返回 0?)然后当数据不可用时它在最后一次读取时无限期阻塞......

Does someone know how to properly use NetworkStream.Read to read data until no more data is available? I guess what I'm wishing for is something like the old Win32 winsock timeout properties... ReadTimeout, etc. It tries to read until the timeout is reached, and then return 0... But it sometimes seem to return 0 when data should be available (or on the way.. can Read return 0 if is available?) and it then blocks indefinitely on the last read when data is not available...

是的,我不知所措!

推荐答案

设置底层套接字 ReceiveTimeout 属性即可解决问题.您可以像这样访问它:yourTcpClient.Client.ReceiveTimeout.您可以阅读文档了解更多信息信息.

Setting the underlying socket ReceiveTimeout property did the trick. You can access it like this: yourTcpClient.Client.ReceiveTimeout. You can read the docs for more information.

现在代码只会在需要一些数据到达套接字时休眠",或者如果在读取操作开始时超过 20 毫秒没有数据到达,它将引发异常.如果需要,我可以调整这个超时.现在我没有在每次迭代中支付 20 毫秒的价格,我只在最后一次读取操作中支付.由于我在从服务器读取的第一个字节中有消息的内容长度,因此我可以使用它来进一步调整它,并且如果已经收到所有预期数据,则不会尝试读取.

Now the code will only "sleep" as long as needed for some data to arrive in the socket, or it will raise an exception if no data arrives, at the beginning of a read operation, for more than 20ms. I can tweak this timeout if needed. Now I'm not paying the 20ms price in every iteration, I'm only paying it at the last read operation. Since I have the content-length of the message in the first bytes read from the server I can use it to tweak it even more and not try to read if all expected data has been already received.

我发现使用 ReceiveTimeout 比实现异步读取容易得多……这是工作代码:

I find using ReceiveTimeout much easier than implementing asynchronous read... Here is the working code:

string SendCmd(string cmd, string ip, int port)
{
  var client = new TcpClient(ip, port);
  var data = Encoding.GetEncoding(1252).GetBytes(cmd);
  var stm = client.GetStream();
  stm.Write(data, 0, data.Length);
  byte[] resp = new byte[2048];
  var memStream = new MemoryStream();
  var bytes = 0;
  client.Client.ReceiveTimeout = 20;
  do
  {
      try
      {
          bytes = stm.Read(resp, 0, resp.Length);
          memStream.Write(resp, 0, bytes);
      }
      catch (IOException ex)
      {
          // if the ReceiveTimeout is reached an IOException will be raised...
          // with an InnerException of type SocketException and ErrorCode 10060
          var socketExept = ex.InnerException as SocketException;
          if (socketExept == null || socketExept.ErrorCode != 10060)
              // if it's not the "expected" exception, let's not hide the error
              throw ex;
          // if it is the receive timeout, then reading ended
          bytes = 0;
      }
  } while (bytes > 0);
  return Encoding.GetEncoding(1252).GetString(memStream.ToArray());
}

这篇关于从 .NET 中的 NetworkStream 读取的正确方法是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆