【问题标题】:C# Networkstream.read()C# Networkstream.read()
【发布时间】:2009-09-01 22:43:52
【问题描述】:

read(buffer, offset, length) 是如何实际工作的,如果我将长度读取为 32,这是否意味着它会一直阻塞直到收到 32 个字节?

我知道它会在套接字异常或连接关闭的情况下分别返回和异常或 0。

如果发送者只发送 31 个字节,read 会继续阻塞吗? 如果这是真的,这是否意味着 read 将始终返回等于传递给它的长度的整数?以及如果剩余的 1 个字节在一定时间后没有出现,我如何控制超时。

重要但未回答

相比之下,如果发送方发送 32 字节,这是否确保读取会阻塞,直到收到所有 32 字节,或者它可以在不读取所有 32 字节的情况下输出。

【问题讨论】:

  • 我添加了示例代码,演示 Read 不会阻塞等待填充整个缓冲区。如果您不发送任何内容,是的,它会阻塞,但如果您发送至少一个字节,它将读取并返回。
  • 你如何回答问题的第二部分,如果我发送 32 个字节并且我在 read 方法中指定了 32 个字节的长度,这是否确保它肯定会读取 32 个字节?我的意思是它会阻塞直到收到 32 个字节。
  • @Kazoom,我认为这个问题已经在两个不同的帖子及其包含的链接中得到了回答。它将阻塞,直到收到至少一个字节。

标签: c# .net sockets


【解决方案1】:

不,它不会阻塞。 Read 操作读取尽可能多的数据,最多为 size 参数指定的字节数。 来源:http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.read.aspx

鉴于它不会等待额外的 1 个字节,如果您期望它,您应该实现一个循环以继续读取流。您可以按照自己的感觉退出循环。


更新: 当我说“根本没有阻塞。如果没有数据可供读取,Read 方法返回 0”时我错了,但是当我说它不会阻塞等待填充整个缓冲区时我是正确的 这是 Kazoom 问题中描述的场景。

更新以演示 NetworkStream.Read 阻塞等待第一个字节,但不阻塞等待填充整个缓冲区

创建到控制台项目

一方面,你有听众:


IPEndPoint ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
TcpListener listener = new TcpListener(ep);
listener.Start();
TcpClient client = listener.AcceptTcpClient();
NetworkStream s = client.GetStream();
byte[] buffer = new byte[32];
Console.WriteLine(s.Read(buffer, 0, 32));
Console.WriteLine("Press any key to continue...");
Console.Read();

在另一端,我们只发送一个字节:


IPEndPoint ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);
TcpClient client = new TcpClient();
client.Connect(ep);
client.GetStream().Write(new byte[] { 60 }, 0, 1);
Console.WriteLine("Press any key to continue...");
Console.Read();

双方将一直运行,直到到达 Console.Read()。请注意,侦听器不会在读取时阻塞。

监听器将打印“1”

【讨论】:

  • 所以它在字节级别而不是位级别阻塞
  • 完全没有阻塞。如果没有数据可供读取,Read 方法返回 0。
  • 没有可用数据是什么意思,如果发送方发送 32 个字节会怎样保证读取只会在读取这 32 个字节后返回,(考虑到这是一个 tcp 连接)
  • 你错了。它没有声明它会阻塞,但它会像任何其他套接字读取一样。
  • 我必须找到我几年前写的一些代码。根据我的记忆,我不得不将 Read 放入一个循环中,因为在某些情况下我没有一次收到我期望的所有字节。我会回复你的。
【解决方案2】:

它将阻塞直到收到 32 个字节,或者连接关闭。异步方法 BeginRead() 和 EndRead() 应该用于提供非阻塞读取。

这里有一些示例代码清楚地展示了阻塞效果。

   Socket one = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    Socket two = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    IPEndPoint ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 12345);

    one.Bind(ep);
    one.Listen(1);
    two.Connect("127.0.0.1", 12345);
    one = one.Accept();

    NetworkStream s = new NetworkStream(two);
    byte[] buffer = new byte[32];
    s.Read(buffer, 0, 32);

编辑

尽管这段代码产生了阻塞效果,但这只是因为 NetworkStream 实现了Stream's abstract Read() method(必须被覆盖)。 Stream.Read() 上的文档说明了这一点:

实现返回的数量 读取的字节数。返回值为零 仅当该位置当前位于 流的结尾。

这就是为什么当没有接收到数据并且没有到达流的末尾时代码块的原因。它还继续说:

实施将一直阻塞,直到 至少一个字节的数据可以是 读取,如果没有数据 可用的。读取仅在以下情况下返回 0 流中没有更多数据 并且没有更多的预期(例如 关闭的套接字或文件结尾)。 安 实现是免费的,返回更少 比请求的字节数即使结束 尚未达到流的最大数。

【讨论】:

  • 只是想知道为什么函数需要返回读取的字节数,而不是只返回读取的状态
  • @Kazoom,如果你读取了20个字节并且关闭了连接,返回的数字是20。
  • 问题是如果他在期望 32 时收到 31 字节会发生什么。你的第一个例子是错误的。您忘记接受连接并且您忘记至少发送一个字节。
  • @Alfred,我更新了我的答案以在其中添加接受。它仍然没有发送一个字节,但这在我的编辑中进行了解释。
【解决方案3】:

关于超时的问题似乎仍然没有答案。

答案是您可以设置stream.ReadTimeout 和stream.WriteTimeout,其中stream 是您的NetworkStream 对象。这处理了根本没有响应的阻塞情况。如果不设置这些值,流将无限期等待。

【讨论】:

    猜你喜欢
    • 2016-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-30
    • 1970-01-01
    • 1970-01-01
    • 2012-03-27
    • 1970-01-01
    相关资源
    最近更新 更多