草庐IT

c# - 分段长度前缀导致从缓冲区读取的下一个数据使用不正确的消息长度

coder 2023-09-18 原文

我是那些来这里寻找其他人所问问题答案的人之一,我想我自己新问了些什么,但是经过两天的搜索,没有成功,我决定是时候自己问一些问题了。所以在这里…
我有一个用C,.NET 4编写的TCP服务器和客户端,使用SocketAsyncEventArgs异步套接字。我有一个以长度为前缀的消息帧协议。总的来说一切都很好,但有一个问题一直困扰着我。
情况是这样的(我将使用小数字作为示例):
假设服务器的发送缓冲区长度为16字节。
它发送一条6字节长的消息,并以4字节长的前缀作为前缀。总消息长度为6+4=10。
客户机读取数据并接收16个字节长度的缓冲区(是的,10个字节的数据,6个字节等于零)。
接收的缓冲区如下:6 0 0 0 56 21 33 1 5 7 0 0 0 0 0
所以我读了前面的4个字节,这是我的长度前缀,我确定我的消息是6个字节长,我也读了它,到目前为止一切都很好。然后我还有16-10=6个字节要读。它们都是0,我读了4个,因为这是我的长度前缀。所以这是一个零长度的消息,它被允许作为keep-alive包。
要读取的剩余数据:0 0
现在问题“开始”。我只剩下2个字节要读,它们不足以完成一个4字节长的前缀缓冲区。所以我读取这两个字节,然后等待更多的输入数据。现在服务器不知道我还在读取长度前缀(我只是在读取缓冲区中的所有零)并发送另一条前缀为4字节的消息。客户机假设服务器发送了丢失的2个字节。我在客户端接收数据,并读取前两个字节以形成一个完整的4字节长的缓冲区。结果是这样的
lengthBuffer=新字节[4]{0,0,42,0}
然后转换成2752512信息长度。所以我的代码将继续读取接下来的2752512字节来完成消息…
所以在每一个消息帧示例中,我都看到零长度的消息被支持为keep-alive,而我看到的每一个示例所做的都比我所做的要少。问题是,当我从服务器接收数据时,我不知道需要读取多少数据。因为我已经用零部分填充了缓冲区,所以我必须读取它,因为这些零可能是我从连接的另一端发送的keep alive。
我可以删除零长度的消息,并在第一条空消息之后停止读取缓冲区,它应该可以解决此问题,并为我的keep alive机制使用自定义消息。但是我想知道我是否遗漏了一些东西,或者做了一些错误的事情,因为我看到的每个代码示例似乎都有相同的问题(?)
更新
马克·格雷弗尔,先生,你把话从我嘴里说了出来。即将更新发送数据时出现的问题。问题是,最初在探索.NET套接字和SocketAsyncEventArgs时,我遇到了这个示例:http://archive.msdn.microsoft.com/nclsamples/Wiki/View.aspx?title=socket%20performance
它使用可重用的缓冲池。只需预先定义允许的最大客户机连接数(例如10),就可以获得最大单个缓冲区大小(例如512),并为所有连接创建一个大缓冲区。所以512*10*2(发送和接收)=10240
所以我们有byte[]buff=newbyte[10240];
然后为每个连接的客户机分配一个这个大缓冲区。第一个连接的客户机为数据读取操作获取前512个字节,为数据发送操作获取后512个字节(偏移量512)。因此,代码最终已经分配了大小为512的发送缓冲区(确切地说,是客户机以后作为bytesTransferred接收的数字)。这个缓冲区填充了数据,512字节中的所有剩余空间都以零的形式发送。
很奇怪,这个例子来自msdn。之所以有一个巨大的缓冲区是为了避免零碎的堆内存,当缓冲区被固定并且GC不能收集它或者类似的东西时。
BufferManager.cs在提供的示例中的注释(请参见上面的链接):
这个类创建一个大的缓冲区,它可以被划分为
分配给SocketAsyncEventArgs对象以用于每个套接字I/O
操作。这使得buffer可以很容易地被重复使用和使用。
防止堆内存碎片化。
所以问题很明显。关于我应该如何解决这个问题的任何建议都是受欢迎的:)他们所说的碎片堆内存是真的吗,是否可以“动态”创建一个数据缓冲区?如果是这样,当服务器扩展到几百甚至数千个客户机时,我会有内存问题吗?

最佳答案

这听起来就像是发送或接收代码中的错误。您应该只得到实际发送的数据,或者如果是以片段的形式到达,那么应该得到比这小一些的数字。我想知道的第一件事是:你设置的发送是否正确?例如,如果缓冲区过大,则正确的实现可能如下所示:

args.SetBuffer(buffer, 0, actualBytesToSend);
if (!socket.SendAsync(args)) { /* whatever */ }

其中,BytesTransferred可以远小于actualBytesToSend。我最初的怀疑是
你在做这样的事情:
args.SetBuffer(buffer, 0, buffer.Length);

因此发送的数据比实际填充的要多。
我要强调的是:无论是发送还是接收,都有一些问题;我不相信,至少没有一个例子,BCL中存在一些基本的底层错误-我广泛使用异步API,而且它工作正常-但您确实需要准确地跟踪您发送的数据i所有点的NG和接收。

关于c# - 分段长度前缀导致从缓冲区读取的下一个数据使用不正确的消息长度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12297468/

有关c# - 分段长度前缀导致从缓冲区读取的下一个数据使用不正确的消息长度的更多相关文章

  1. ruby-on-rails - unicode 字符串的长度 - 2

    在我的Rails(2.3,Ruby1.8.7)应用程序中,我需要将字符串截断到一定长度。该字符串是unicode,在控制台中运行测试时,例如'א'.length,我意识到返回了双倍长度。我想要一个与编码无关的长度,以便对unicode字符串或latin1编码字符串进行相同的截断。我已经了解了Ruby的大部分unicode资料,但仍然有些一头雾水。应该如何解决这个问题? 最佳答案 Rails有一个返回多字节字符的mb_chars方法。试试unicode_string.mb_chars.slice(0,50)

  2. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  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 - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

  5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  6. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  7. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  8. 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中的所有其他对象

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

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

  10. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

    我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

随机推荐