如何防止 HttpWebRequest 的数据包碎片
Posted
技术标签:
【中文标题】如何防止 HttpWebRequest 的数据包碎片【英文标题】:How to prevent packet fragmentation for a HttpWebRequest 【发布时间】:2011-01-13 09:59:54 【问题描述】:我在针对嵌入式设备上的 HTTP 守护程序使用 HttpWebRequest 时遇到问题。问题似乎是在写入套接字流的 http 标头和 http 有效负载(POST)之间有足够的延迟,以至于套接字将套接字缓冲区中的内容释放到服务器。这会导致 HTTP 请求被分成两个数据包(碎片)。
当然,这是完全有效的,但是如果数据包被拆分超过大约 1.8 毫秒,另一端的服务器将无法处理它。所以我想知道是否有任何现实的方法来控制这个(在客户端)。
在 HttpWebRequest 上似乎没有任何属性可以对用于发送的套接字提供这种级别的控制,并且似乎无法访问套接字本身(即通过反射),因为它只是在发送,然后发布(作为出站 http 连接池的一部分)。 BufferWriteStream 属性只是缓冲 webrequest 中的正文内容(因此它仍然可用于重定向等......),并且似乎不会影响整个请求写入套接字的方式。
那该怎么办?
(我真的在努力避免从套接字重新编写 HTTP 客户端)
一种选择可能是编写某种代理,将 HttpWebRequest 发送到(可能通过 ServicePoint),并在该实现中缓冲整个 TCP 请求。但这似乎是一项艰巨的工作。
当我运行 Fidder 时它也可以正常工作(出于同样的原因),但这在我们的生产环境中并不是一个真正的选择......
[ps:我知道分片数据包之间的间隔肯定是问题所在,因为我完成了一个套接字级别的测试,在该测试中我使用 NoDelay 套接字显式控制了分片]
【问题讨论】:
你做了完美的工作来理解这个问题。您唯一忘记的是服务器。它的行为是异常的,它必须在超时间隔(大约 20-100 秒)内接收所有数据包。因为它是一个 RFC 标准。是否有可能修复服务器? 我已经向设备供应商询问了这一点,但作为嵌入式设备,我怀疑这可能会变得复杂,这就是我试图找到客户端修复程序的原因。 【参考方案1】:仅查看客户端拆分数据包问题,我发布了一个与我自己的问题相关的答案:
我在这里看到了答案:
http://us.generation-nt.com/answer/too-packets-httpwebrequest-help-23298102.html
【讨论】:
【参考方案2】:最后,供应商推出了包含新版本 HTTPD 的固件升级,问题就消失了。他们使用的是 BusyBox linux,显然他们遇到的 HTTPD 实现还有其他一些问题。
就我最初的问题而言,除了编写套接字代理之外,我认为没有任何可靠的方法可以做到这一点。我在上面玩过的一些解决方法是靠运气而不是设计(因为它们意味着 .net 一次性发送了整个数据包)。
【讨论】:
【参考方案3】:似乎已解决的问题是禁用与该 URI 关联的 ServicePoint 上的 Nagling,并将请求作为 HTTP 1.0 发送(它们似乎都无法修复它):
var servicePoint = ServicePointManager.FindServicePoint(uri.Uri);
servicePoint.UseNagleAlgorithm = false;
但是,这似乎仍然只是通过使请求更快发出而不是强制标头和有效负载作为一个数据包写入来解决此问题。所以它可能会在加载的机器/高延迟链接等上失败。
想知道编写碎片整理代理有多难......
【讨论】:
您是否也禁用了 Expect100Continue。虽然可能与嵌入式服务器无关,但我发现这两个属性受一些未记录的规则的约束,例如对我来说,Epect100Continue = true 在某些情况下会禁用 naggle 算法,尽管该属性设置为 true。另请查看 SupportsPipelining。我怀疑关闭它会立即关闭连接而不是让它保持打开状态,这在嵌入式服务器上可能不是一个好主意。【参考方案4】:您的嵌入式服务器是 HTTP/1.1 服务器吗?如果是这样,请在调用 GetRequestStream() 之前尝试在 webrequest 上设置 Expect100Continue=false。这将确保 HTTP 堆栈在发送实体正文之前不会期望来自服务器的“HTTP/1.1 100 continue”标头响应。因此,即使数据包仍会在标头和正文之间拆分,数据包之间的间隙也会更短。
【讨论】:
通过将我的请求发布为 HTTP 1.0 我已经禁用了它(我假设您的意思只是将 'Expect' 标头清空,因为请求上没有明确的 Expect100Continue 属性)。 对不起,我的意思是设置 ServiecPoint.Expect100Continue=false。无论如何,我看到您有解决方案。但是,我同意以前的评论者的观点,即您的服务器行为不正确。它不应该要求请求和实体进入同一个数据包。如果您可以发布请求/响应的网络捕获 (Wireshark)(在您的更改之前),我们可以看到到底发生了什么,这可能有助于找出解决方案应该是什么。以上是关于如何防止 HttpWebRequest 的数据包碎片的主要内容,如果未能解决你的问题,请参考以下文章
避免 HttpWebRequest GET 和 ProtocolViolationException
如何使用 HttpWebRequest 将数据发布到 MVC 控制器?
如何使用 c# 使用 httpwebrequest 从 json api 获取数据?
如何从 Silverlight 中的 HttpWebRequest.BeginGetRequestStream 更新我的 UI