1

最近我开始使用套接字。我意识到,当从网络流中读取数据时,您无法知道有多少数据进入。因此,您要么事先知道必须接收多少字节,要么知道哪些字节

由于我目前正在尝试实现 C# WebSocket服务器,因此我需要处理 HTTP 请求。HTTP 请求可以有任意长度,因此事先知道多少字节是不可能的。但是一个 HTTP 请求总是有一定的格式。它从请求行开始,然后是零个或多个标头等。所以有了所有这些信息,它应该很简单,对吧?

没有。

我想出的一种方法是读取所有数据,直到识别出特定的字节序列。StreamReader 类有一个ReadLine方法,我相信它是这样工作的。对于 HTTP,一个合理的分隔符是分隔消息头和正文的空行。

这里明显的问题是需要一个(最好是短的)终止序列,比如换行符。甚至 HTTP 规范也建议这两个相邻的 CRLF 不是一个好的选择,因为它们也可能出现在消息的开头。毕竟,两个 CRLF 无论如何都不是一个简单的分隔符。

因此,将该方法扩展到任意类型 3 语法,我得出结论,解析数据的最佳选择是有限状态机。我可以一个字节一个字节地将数据提供给机器,就像我从网络流中读取它一样。一旦机器接受输入,我就可以停止读取数据。此外,FSM 可以立即捕获重要的令牌。

但这真的是最好的解决方案吗?逐个字节地读取并使用自定义解析器对其进行验证似乎既乏味又昂贵。FSM 要么很慢,要么很丑。所以...

当形式已知但大小未知时,如何处理来自网络流的数据?

像HttpListener这样的类如何解析消息并快速处理呢?

我在这里错过了什么吗?这通常会怎么做?

4

2 回答 2

4

HttpListener和其他此类组件可以解析消息,因为格式是确定性的。请求有据可查。请求标头是一系列以 CRLF 结尾的行,后跟一个空行(一行中的两个 CRLF)。

消息体可能难以解析,但它是确定性的,因为标头会告诉您使用什么编码、是否压缩等。即使是多部分消息也不是很难解析。

是的,您确实需要一个状态机来解析 HTTP 消息。是的,您必须逐字节解析它。它有点涉及,但它非常快。通常,您将流中的一堆数据读入缓冲区,然后逐字节处理该缓冲区。您不会一次读取一个字节的流,因为开销会降低性能。

您应该查看HttpListener源代码以了解它是如何工作的。访问http://referencesource.microsoft.com/netframework.aspx并下载 .NET 4.5 Update 1 源代码。

准备好花费大量时间来挖掘它和 HTTP 规范。

顺便说一句,创建一个处理一小部分 HTTP 请求的程序并不难。但是我想知道当您可以使用HttpListener并为您处理所有细节时为什么要这样做。

更新

您在谈论两种不同的协议。HTTP 和WebSocket是两个完全不同的东西。正如维基百科文章所说:

WebSocket 协议是一个独立的基于 TCP 的协议。它与 HTTP 的唯一关系是它的握手被 HTTP 服务器解释为升级请求。

使用 HTTP,您知道服务器将发送流然后关闭连接;它是具有定义结束的字节流。WebSocket 是一种基于消息的协议;它启用消息流。必须以某种方式描述这些信息;发送者必须告诉接收者消息的结尾在哪里。这可以是隐含的或明确的。有几种不同的方法可以做到这一点:

  1. 发送者在消息的前几个字节中包含消息的长度。例如,前四个字节是一个二进制整数,表示该消息中有多少字节。所以接收器读取前四个字节,将其转换为整数,然后读取那么多字节。
  2. 消息的长度是隐含的。例如,发送方和接收方同意所有消息的长度为 80 字节。
  3. 消息的第一个字节是消息类型,每个消息类型都有定义的长度。例如,消息类型 1 是 40 字节,消息类型 2 是 27 字节,等等。
  4. 消息有一些终结符。例如,在面向行的消息系统中,消息由 CRLF 终止。发件人发送文本,然后发送 CRLF。接收方读取字节,直到接收到 CRLF。

无论如何,发送者和接收者必须就消息的结构达成一致。否则,您担心的情况确实会出现:接收者等待永远不会收到的字节。

为了处理可能的通信问题,您在套接字上设置了ReceiveTimeout属性,以便在接收完整消息的时间过长时Read会抛出异常。SocketException这样,您的程序就不会无限期地等待未到来的数据。但这应该只发生在通信问题的情况下。任何合理的消息格式都将包括确定消息长度的方法;要么你知道有多少数据即将到来,要么你知道你什么时候到达了消息的末尾。

于 2013-09-02T03:23:40.167 回答
0

如果你想发送一条消息,你可以在它前面加上消息的大小。获取消息中的字节数,在其前面附加一个 ulong。在接收器处,读取 ulong 的大小,对其进行解析,然后从流中读取该数量的字节,然后将其关闭。

在 HTTP 标头中,您可以阅读: Content-Length 以八位字节(8 位字节)为单位的请求正文的长度

于 2013-09-02T00:28:48.623 回答