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

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

问题描述

我一直挣扎着这一点,并不能找到一个理由,为什么我的code没能正确的我也写一个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());
}

最后一个作品,但它肯定看起来很丑把一个硬codeD睡眠循环考虑到插座已经支持读取超时里面!我是否需要安装在的NetworkStream的TcpClient的某些属性(IES)?问题是否驻留在服务器上?服务器不关闭的连接,它是由客户端这样做。上面也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时数据应该可以(或者在路上......可以读返回0,如果是可用的?),它再无限期地阻塞在最后一次读取数据时不可用......

Do someone knows 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.

现在的code只会休眠只要需要一些数据在插座到达,或者,如果没有数据到达时,它会抛出一个异常,在读操作开始时,超过20ms 。如果需要,我可以调整此超时。现在,我不付20ms的价格在每次迭代中,我只支付其在最后一读操作。由于我在从服务器读取的第一个字节的信息的内容长度我可以用它来调整它更不尝试,如果所有预期的数据已被接收的读取。

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远比实现异步读取更加容易......这是工作code:

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());
}

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

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