草庐IT

c# - 连续调用 NetworkStream.Write - 它有什么不同吗?

coder 2023-09-19 原文

考虑以下两种发送数据的方法和一种读取数据的方法:

public static void SendConsecutively(this NetworkStream stream)
{
    byte[] header = {1, 2, 3, 4};
    byte[] message = {5, 6, 7, 8, 9, 10};
    stream.Write(header, 0, header.Length);
    stream.Write(message, 0, message.Length);
}

public static void SendAllInOne(this NetworkStream stream)
{
    byte[] headerAndMessage = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    stream.Write(headerAndMessage, 0, headerAndMessage.Length);
}

public static byte[] ReadMessage(this NetworkStream stream)
{
    byte[] data = new byte[10];
    int bytesRead = stream.Read(data, 0, 10);
    return data;        
}

与不拆分数据(如 SendAllInOne)相比,是否将数据拆分为两个 block (如 SendConsecutively)有区别吗?

问题是,在我的测试中,ReadMessage 总是读取 10 个字节。我向其发送 header 和消息(但不知道它是如何实现的)的第 3 方服务器通常也会收到 10 个字节。但有时 - 在极少数情况下 - 他们告诉我第 3 方服务器只收到 4 个字节。我必须处理这个问题,即该服务器只收到 4 个字节,尽管我确信我已经使用 SendConsecutively 发送了 10 个字节。 (因为我的日志中没有异常,这意味着 stream.Write 调用肯定已经发出。)

因此,一个问题是:两次连续的 stream.Write 调用之间发生了什么?我想:没什么,因为 NetworkStream.Flush 的文档说:“Flush 方法实现了 Stream.Flush 方法;但是,因为 NetworkStream 没有缓冲,所以它对网络流没有影响。” [1]

我收到了 Wireshark 日志,其中可以看到一个 TCP 数据包仅包含四个 header 字节。每个 stream.Write 调用都会产生一个 TCP 数据包吗?但话又说回来,我什至需要关心我的数据是如何拆分成 TCP 数据包的吗?因为大消息无论如何都会被分割成多个 TCP 数据包,不是吗? (是的,也有发送的大消息比我在上面的示例中使用的 6 字节大得多 - 例如:4 字节 header + 3000 字节消息)

我的 SendConsecutively 方法是否有任何缺陷,可能会在接收端导致此类问题(同时考虑到 message 可能是 3000 字节,而不是像示例代码)?

难道是第 3 方服务器的问题?如果该服务器像上面的 ReadMessage 方法一样实现,则可能会出现问题。因为有时(我认为这一定会发生,当消息通过 TCP 分段时),ReadMessage 读取的字节数少于已发送的字节数,并且返回的 bytesRead 数字小于实际的字节数消息的长度。消息的其余部分稍后在流中可用(几毫秒?)。对此有什么想法吗?

[1] MSDN,NetworkStream.Flush,https://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.write(v=vs.110).aspx

最佳答案

网络流是一种抽象——它隐藏了实现细节。通常开发人员可以忽略发送数据的传输方式——多个数据包或单个数据包,使用哪种协议(protocol)等。但是,重要的是(至少对我来说,可能对每个使用网络流的人来说)记住一些关于 I/O 的事实:

  • 通常保证数据的顺序(它按照写入的顺序接收)
  • 可以随时中断(通过抛出异常)
  • 同步读取可能会无限期地阻塞(大多数情况下不是,但有机会)
  • I/O 比其他指令慢得多
  • 数据 block 的大小在流的不同端是不同的
  • 频繁写入小块的效率可能低于写入一个大块(由于底层传输层导致的额外负载)
  • 读取数据比写入数据慢可能会溢出内部缓冲区

这些事实中的大部分并不取决于电线两端使用的技术。它可以在一侧是 Java,在另一侧是 .NET - 通常只要 channel 只是一个字节流就没有关系。

考虑到这一点,您的问题的答案:

Does it make a difference whether or not to split the data into two chunks (like in SendConsecutively) compared to not splitting the data (like in SendAllInOne)? ... What happens between two consecutive stream.Write calls? ... Is my SendConsecutively method flawed in any way which could cause such problems on the receiver-side (Also considering that message could be 3000 bytes, not just 6 like in the example code)?

根据套接字的内部实现可能会有所不同。 At 可能导致两次连续发送或合并为一次发送。 NetworkStream发送调用底层套接字的发送。这个调用被转换为套接字的 Send() 然后它真的取决于 WinSock's send 的实现.

对于接收者来说,数据发送的准确程度并不重要。网络层可以将数据拆分成不同的 block ,而与发送 block 的大小无关。

关于c# - 连续调用 NetworkStream.Write - 它有什么不同吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49635260/

有关c# - 连续调用 NetworkStream.Write - 它有什么不同吗?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  4. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  5. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  6. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  7. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  8. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  9. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  10. ruby - 为什么 SecureRandom.uuid 创建一个唯一的字符串? - 2

    关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?

随机推荐