jjzjj

c# - 如果我们不从 TcpClient 流中读取,接收到的数据会发生什么

coder 2023-09-19 原文

我很好奇 tcp 流中的一个案例。

假设我们创建了一个 TcpClient 并将一些有意义的请求字符串写入传出流。例如一个 Http 请求。

try
        {
            string requestString = "GET /Api/Test HTTP/1.1 \r\n" +
                                   "Host: 192.168.2.45 \r\n" +
                                   "Connection:close \r\n\r\n";

            TcpClient client = new TcpClient();

            client.Connect(new IPEndPoint(IPAddress.Parse("192.168.2.45"), 80));

            NetworkStream stream = client.GetStream();

            byte[] reqBuffer = System.Text.Encoding.Default.GetBytes(requestString);

            stream.Write(reqBuffer, 0, reqBuffer.Length);

因此我们的 NIC 会立即从目标套接字收到响应。

这是我的问题:

  1. 传入的字节存储在哪里? (NIC有存储,还是存储在RAM中?)
  2. 如果我们不从应用程序中读取这些字节,我们的 NIC 将如何处理这些字节。

阅读:

if (stream.CanRead)
{
   bufferInt = stream.Read(buffer, 0, client.ReceiveBufferSize);
}

最佳答案

与许多其他事物一样,答案在于分层。

那么,让我们从硬件开始:

所有 NIC 都有一些内部缓冲区。这是组装任何响应的第一个位置——但它也是像 TCP 这样的东西并没有多大意义的层次; NIC 只关心它自己的网络协议(protocol),例如以太网或 PPP。在这个层面上,IP 只是一个无差别的有效负载,而 IP 又将 TCP 作为有效负载(尽管应该注意的是,分层远非完美:) 例如,TCP 和 IP 之间存在很多耦合)。

必须先解释传入的数据,然后才能执行任何操作;让我们跳过细节,假设 NIC 缓冲区现在包含一个漂亮的小 TCP/IP 数据包。现在,NIC 驱动程序开始发挥作用——您机器上每个打开的端口都有一个相关联的内存块用于接收数据。基本上,这是您在设置 ReceiveBufferSizeSendBufferSize 时所控制的。驱动程序将指示 NIC 如何处理传入的数据 - 通常,NIC 会使用 DMA 将数据直接发送到 RAM。这非常快——现代 NIC 并不真的需要大型板载内存芯片;即使对于服务器 NIC,该数量通常也在 32 MiB 左右。

这两个 RAM 缓冲区对于您的问题来说是最重要的——当它们已满时,NIC 将简单地丢弃任何进一步到达的数据包。对于具有流量控制的 TCP,它会告诉对方停止传输,谢谢。实际上,这模拟了缓冲流的通常行为——发送方将被阻塞,直到可以再次发送另一条数据为止。发生这种情况时,发送方将重传上次没有成功的数据。对于像 UDP 这样的协议(protocol),没有流量控制,也没有重传,因此您只会不可挽回地丢失数据(甚至没有告诉您有问题)。

如果您有待处理的发送/接收操作(例如 NetworkStream.Read),您还将使用自己的缓冲区 - 这是另一层,但它确实是最不重要的。这里发生的一切是,当操作系统从 NIC 驱动程序获取信息时,它会用内部缓冲区的数据填充您的缓冲区并向您发出信号。在同步场景中(如您的情况),这只会导致您的 Read 调用返回。异步场景在 .NET 和 OS 交互以产生回调中有些棘手,但其余部分几乎相同。例如,这与从本地硬盘驱动器读取文件没有根本区别。

在 .NET 中需要注意的一件重要事情是,您在这些发送/接收中使用的缓冲区在操作期间固定。这意味着缓冲区被禁止在内存中移动,这会降低垃圾收集器的效率(阻止适当的堆压缩);如果您有很多长时间运行的操作,您确实希望尽可能多地重用缓冲区 - 如果您总是为每个操作创建一个新缓冲区,您可能会遇到堆碎片问题。

关于c# - 如果我们不从 TcpClient 流中读取,接收到的数据会发生什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33807443/

有关c# - 如果我们不从 TcpClient 流中读取,接收到的数据会发生什么的更多相关文章

  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 - 什么是填充的 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%

  4. 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

  5. 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返

  6. ruby-on-rails - 如果为空或不验证数值,则使属性默认为 0 - 2

    我希望我的UserPrice模型的属性在它们为空或不验证数值时默认为0。这些属性是tax_rate、shipping_cost和price。classCreateUserPrices8,:scale=>2t.decimal:tax_rate,:precision=>8,:scale=>2t.decimal:shipping_cost,:precision=>8,:scale=>2endendend起初,我将所有3列的:default=>0放在表格中,但我不想要这样,因为它已经填充了字段,我想使用占位符。这是我的UserPrice模型:classUserPrice回答before_val

  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方法创建的字符串从不重复?

随机推荐